summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore9
-rw-r--r--README24
-rw-r--r--config/mysql.properties.example6
-rw-r--r--pom.xml90
-rw-r--r--src/main/java/org/openslx/imagemaster/App.java46
-rw-r--r--src/main/java/org/openslx/imagemaster/db/DbUser.java30
-rw-r--r--src/main/java/org/openslx/imagemaster/db/MySQL.java80
-rw-r--r--src/main/java/org/openslx/imagemaster/server/ApiServer.java65
-rw-r--r--src/main/java/org/openslx/imagemaster/session/Authenticator.java31
-rw-r--r--src/main/java/org/openslx/imagemaster/session/Session.java58
-rw-r--r--src/main/java/org/openslx/imagemaster/session/SessionManager.java73
-rw-r--r--src/main/java/org/openslx/imagemaster/session/User.java48
-rw-r--r--src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java36
-rw-r--r--src/main/java/org/openslx/imagemaster/thrift/server/ImageServerHandler.java35
-rw-r--r--src/main/java/org/openslx/imagemaster/thrift/server/TBinaryProtocolSafe.java121
-rw-r--r--src/main/java/org/openslx/imagemaster/util/Hash.java100
-rw-r--r--src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java492
-rw-r--r--src/main/java/org/openslx/imagemaster/util/Util.java16
-rw-r--r--src/main/thrift/imagemaster.thrift59
-rw-r--r--src/test/java/org/openslx/imagemaster/AppTest.java38
-rwxr-xr-xthrift-compile.sh7
21 files changed, 1464 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d6fcdeb
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+/testing
+/.settings
+/.project
+/.classpath
+/config/mysql.properties
+/target
+/src/main/java/org/openslx/imagemaster/thrift/iface
+/gen-java
+
diff --git a/README b/README
new file mode 100644
index 0000000..835d693
--- /dev/null
+++ b/README
@@ -0,0 +1,24 @@
+1. Import project in eclipse (requires m2e)
+Import -> Maven -> Existing Maven Project
+Eclipse will complain about missing source files/classes.
+They need to be generated...
+
+2. Install the thrift compiler
+Prequisites:
+apt-get install libboost-dev libboost-test-dev libboost-program-options-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev
+Thrift 0.9.1 (current as of writing):
+http://www.apache.org/dyn/closer.cgi?path=/thrift/0.9.1/thrift-0.9.1.tar.gz
+
+./configure, make, make install
+
+3. Run ./thrift-compile.sh, it will generate the missing
+files mentioned before. Refresh the project in Eclipse.
+
+"Run as -> Maven install..." should work now and create
+a nice *.jar in ./target/
+
+4. Create config/mysql.properties
+
+
+## TODO: Dump db schema
+
diff --git a/config/mysql.properties.example b/config/mysql.properties.example
new file mode 100644
index 0000000..a434dff
--- /dev/null
+++ b/config/mysql.properties.example
@@ -0,0 +1,6 @@
+# Fill in credentials and rename to mysql.properties
+host=localhost
+db=masterserver
+user=masterserver
+password=geheim
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..28192b7
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,90 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.openslx.imagemaster</groupId>
+ <artifactId>image-master</artifactId>
+ <version>1.0-SNAPSHOT</version>
+ <packaging>jar</packaging>
+
+ <name>image-master</name>
+ <url>http://maven.apache.org</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.1</version>
+ <configuration>
+ <source>1.7</source>
+ <target>1.7</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <archive>
+ <manifest>
+ <mainClass>org.openslx.imagemaster.App</mainClass>
+ </manifest>
+ </archive>
+ <descriptorRefs>
+ <descriptorRef>jar-with-dependencies</descriptorRef>
+ </descriptorRefs>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ <version>1.2.17</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.7.5</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.thrift</groupId>
+ <artifactId>libthrift</artifactId>
+ <version>0.9.1</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>mysql</groupId>
+ <artifactId>mysql-connector-java</artifactId>
+ <version>5.1.28</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>fi.evident.dalesbred</groupId>
+ <artifactId>dalesbred</artifactId>
+ <version>0.6.0</version>
+ <scope>compile</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/org/openslx/imagemaster/App.java b/src/main/java/org/openslx/imagemaster/App.java
new file mode 100644
index 0000000..ef04e54
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/App.java
@@ -0,0 +1,46 @@
+package org.openslx.imagemaster;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+import org.openslx.imagemaster.thrift.server.BinaryListener;
+
+/**
+ * Hello world!
+ *
+ */
+public class App
+{
+ private static Logger log = Logger.getLogger( App.class );
+
+ private static List<Thread> servers = new ArrayList<>();
+
+ public static void main( String[] args )
+ {
+ // Init logging
+ BasicConfigurator.configure();
+ log.info( "Starting Application" );
+ // Create binary listener
+ Thread t;
+ t = new Thread(new BinaryListener(), "BinaryListener");
+ servers.add(t);
+ t.start();
+ // Run more servers
+ // ...
+ // Wait for all servers to die
+ for (Thread wait : servers) {
+ boolean success = false;
+ while (!success) {
+ try {
+ wait.join();
+ success = true;
+ } catch ( InterruptedException e ) {
+ // Do nothing...
+ }
+ }
+ }
+ log.info( "All Servers shut down, exiting..." );
+ }
+}
diff --git a/src/main/java/org/openslx/imagemaster/db/DbUser.java b/src/main/java/org/openslx/imagemaster/db/DbUser.java
new file mode 100644
index 0000000..f8400a9
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/db/DbUser.java
@@ -0,0 +1,30 @@
+package org.openslx.imagemaster.db;
+
+import org.openslx.imagemaster.session.User;
+
+
+public class DbUser extends User
+{
+ public DbUser(String username, String password, String organization, String firstName, String lastName, String eMail,
+ String satelliteAddress)
+ {
+ super( username, password, organization, firstName, lastName, eMail, satelliteAddress );
+ }
+
+ /**
+ * Query database for user with given login
+ * @param login (user@organization)
+ * @return instance of DbUser for matching entry from DB, or null if not found
+ */
+ public static DbUser forLogin( final String login )
+ {
+ final String[] parts = login.split( "@" );
+ if ( parts.length != 2 )
+ return null;
+ return MySQL.findUniqueOrNull( DbUser.class,
+ "SELECT user.username, user.password, user.organization, user.firstname, user.lastname, user.email, satellite.address FROM user" +
+ " LEFT JOIN satellite USING (organization)" +
+ " WHERE user.username = ? AND user.organization = ? LIMIT 1", parts[0], parts[1] );
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/db/MySQL.java b/src/main/java/org/openslx/imagemaster/db/MySQL.java
new file mode 100644
index 0000000..15bf5e2
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/db/MySQL.java
@@ -0,0 +1,80 @@
+package org.openslx.imagemaster.db;
+
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.List;
+import java.util.Properties;
+import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
+
+import org.apache.log4j.Logger;
+import org.openslx.imagemaster.util.Util;
+
+import fi.evident.dalesbred.Database;
+
+/**
+ * Class for talking to the DB via the dalesbred jdbc wrapper. Package private,
+ * so only the Db* classes can actually communicate with the database.
+ *
+ */
+class MySQL
+{
+ private static final Logger log = Logger.getLogger( MySQL.class );
+ private static Database db = null;
+
+ static
+ {
+ // Load connection info from class (TODO: Make pretty)
+ Properties properties = new Properties();
+ try {
+ final BufferedInputStream stream = new BufferedInputStream( new FileInputStream( "config/mysql.properties" ) );
+ properties.load( stream );
+ stream.close();
+ } catch ( FileNotFoundException e ) {
+ log.fatal( "config/mysql.properties not found!" );
+ System.exit( 1 );
+ } catch ( IOException e ) {
+ log.fatal( "Error reading from config/mysql.properties: " + e.getMessage() );
+ System.exit( 1 );
+ } catch ( Exception e ) {
+ log.fatal( "Generic error loading mysql properties file." );
+ e.printStackTrace();
+ System.exit( 1 );
+ }
+ final String host = properties.getProperty( "host" );
+ final String dbname = properties.getProperty( "db" );
+ final String user = properties.getProperty( "user" );
+ final String password = properties.getProperty( "password" );
+
+ Util.notNullFatal( host, "host not set in mysql properties" );
+ Util.notNullFatal( dbname, "db not set in mysql properties" );
+ Util.notNullFatal( user, "user not set in mysql properties" );
+ Util.notNullFatal( password, "password not set in mysql properties" );
+
+ // Setup db connection
+ try {
+ MysqlDataSource ds = new MysqlDataSource();
+ ds.setServerName( host );
+ ds.setDatabaseName( dbname );
+ ds.setUser( user );
+ ds.setPassword( password );
+ db = Database.forDataSource( ds );
+ } catch ( Exception e ) {
+ log.fatal( "Error initializing mysql data source!" );
+ e.printStackTrace();
+ System.exit( 1 );
+ }
+ }
+
+ protected static <T> List<T> findAll( final Class<T> clazz, final String sql, final Object... args )
+ {
+ return db.findAll( clazz, sql, args );
+ }
+
+ protected static <T> T findUniqueOrNull( final Class<T> clazz, final String sql, final Object... args )
+ {
+ return db.findUniqueOrNull( clazz, sql, args );
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/server/ApiServer.java b/src/main/java/org/openslx/imagemaster/server/ApiServer.java
new file mode 100644
index 0000000..e62b61b
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/server/ApiServer.java
@@ -0,0 +1,65 @@
+package org.openslx.imagemaster.server;
+
+import org.apache.log4j.Logger;
+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.InvalidTokenException;
+import org.openslx.imagemaster.thrift.iface.SessionData;
+import org.openslx.imagemaster.thrift.iface.UserInfo;
+
+/**
+ * API Server This is where all the requests from the outside arrive. We don't
+ * handle them directly in the Thrift handlers, as we might be adding other APIs
+ * later, like JSON/SOAP/REST/HTTP/XML or some other stuff. They'd all just
+ * interface with this static class here. Note that we use the exceptions from
+ * the thrift interface that you can simply catch in any other API handler and
+ * eg. transform into error codes, if the API doesn't support exceptions.
+ *
+ * This will be accessed from multiple threads, so use synchronization when
+ * needed (or in doubt)
+ */
+public class ApiServer
+{
+ @SuppressWarnings( "unused" )
+ private static Logger log = Logger.getLogger( ApiServer.class );
+ //
+
+ /**
+ * Request for authentication
+ * @param login (username@organization)
+ * @param 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.getSession( token );
+ if ( session == null )
+ throw new InvalidTokenException();
+ return new UserInfo( session.getUserId(), session.getFirstName(), session.getLastName(), session.getEMail() );
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/session/Authenticator.java b/src/main/java/org/openslx/imagemaster/session/Authenticator.java
new file mode 100644
index 0000000..f730c72
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/session/Authenticator.java
@@ -0,0 +1,31 @@
+package org.openslx.imagemaster.session;
+
+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.imagemaster.util.Sha512Crypt;
+
+public class Authenticator
+{
+ private static Logger log = Logger.getLogger( Authenticator.class );
+
+ /**
+ * Authenticate the user against whatever backend... currently MySQL only
+ * @param username
+ * @param password
+ * @return
+ * @throws AuthenticationException
+ */
+ public static User authenticate( String username, String password ) throws AuthenticationException
+ {
+ DbUser user = DbUser.forLogin( username );
+ if ( user == null || !Sha512Crypt.verifyPassword( password, user.password ) ) {
+ log.debug( "Login failed: " + username );
+ throw new AuthenticationException( AuthenticationError.INVALID_CREDENTIALS, "Invalid username or password!" );
+ }
+ log.debug( "Login successful: " + username );
+ return user;
+ }
+ //
+}
diff --git a/src/main/java/org/openslx/imagemaster/session/Session.java b/src/main/java/org/openslx/imagemaster/session/Session.java
new file mode 100644
index 0000000..8dc7f2b
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/session/Session.java
@@ -0,0 +1,58 @@
+package org.openslx.imagemaster.session;
+
+/**
+ * Simple representation of a user session. Contains user-related data and
+ * information on whether the session is still valid.
+ *
+ */
+public class Session
+{
+ private static final long TIMEOUT = 600L * 1000L; // TODO: config
+
+ private long timeOut = 0;
+ private final User user;
+
+ public Session(final User dbuser)
+ {
+ this.user = dbuser;
+ this.timeOut = System.currentTimeMillis() + TIMEOUT;
+ }
+
+ public synchronized void refresh()
+ {
+ if ( timedOut() )
+ return; // Don't allow refreshing timed out session
+ this.timeOut = System.currentTimeMillis() + TIMEOUT;
+ }
+
+ public synchronized boolean timedOut()
+ {
+ return System.currentTimeMillis() > this.timeOut;
+ }
+
+ public String getSatelliteAddress()
+ {
+ return user.satelliteAddress;
+ }
+
+ public String getUserId()
+ {
+ return user.username + "@" + user.organization;
+ }
+
+ public String getFirstName()
+ {
+ return user.firstName;
+ }
+
+ public String getLastName()
+ {
+ return user.lastName;
+ }
+
+ public String getEMail()
+ {
+ return user.eMail;
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/session/SessionManager.java b/src/main/java/org/openslx/imagemaster/session/SessionManager.java
new file mode 100644
index 0000000..cc68d0b
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/session/SessionManager.java
@@ -0,0 +1,73 @@
+package org.openslx.imagemaster.session;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.apache.log4j.Logger;
+import org.openslx.imagemaster.thrift.iface.SessionData;
+import org.openslx.imagemaster.util.Hash;
+
+/**
+ * Class for managing active user sessions. This class and all its function are
+ * (supposed to be) thread-safe.
+ */
+public class SessionManager
+{
+ private static Logger log = Logger.getLogger( SessionManager.class );
+
+ // 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 )
+ {
+ final String authToken = Hash.md5( UUID.randomUUID().toString() );
+ final String sessionId = Hash.sha256( UUID.randomUUID().toString() );
+
+ synchronized ( sessions ) {
+ sessions.put( authToken, session );
+ }
+ return new SessionData( sessionId, authToken, session.getSatelliteAddress() );
+ }
+
+ public static Session getSession( String token )
+ {
+ final Session session;
+ synchronized ( sessions ) {
+ session = sessions.get( token );
+ }
+ if ( session == null || session.timedOut() ) {
+ return null;
+ }
+ return session;
+ }
+
+ static {
+ gcThread = new Thread( new Runnable() {
+ @Override
+ public void run()
+ {
+ 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() ) {
+ log.debug( "Removing old session of " + s.getUserId() );
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+ } );
+ gcThread.start();
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/session/User.java b/src/main/java/org/openslx/imagemaster/session/User.java
new file mode 100644
index 0000000..52c8c78
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/session/User.java
@@ -0,0 +1,48 @@
+package org.openslx.imagemaster.session;
+
+/**
+ * Represents a user. Should be extended and given an according static method to
+ * instantiate by loading data from some backend.
+ *
+ */
+public abstract class User
+{
+
+ public final String username, organization;
+ public final String password;
+ public final String firstName, lastName;
+ public final String eMail;
+ public final String satelliteAddress;
+
+ protected User(String username, String password, String organization, String firstName, String lastName, String eMail,
+ String satelliteAddress)
+ {
+ this.username = username;
+ this.organization = organization;
+ this.password = password;
+ this.firstName = firstName;
+ this.lastName = lastName;
+ this.eMail = eMail;
+ this.satelliteAddress = satelliteAddress;
+ }
+
+ @Override
+ public String toString()
+ {
+ final StringBuilder sb = new StringBuilder( this.username );
+ sb.append( "@" );
+ sb.append( this.organization );
+ sb.append( ": " );
+ sb.append( this.firstName );
+ sb.append( ' ' );
+ sb.append( this.lastName );
+ sb.append( ' ' );
+ sb.append( this.eMail );
+ if ( this.satelliteAddress != null ) {
+ sb.append( ' ' );
+ sb.append( this.satelliteAddress );
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java b/src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java
new file mode 100644
index 0000000..8eeb7bc
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/thrift/server/BinaryListener.java
@@ -0,0 +1,36 @@
+package org.openslx.imagemaster.thrift.server;
+
+import org.apache.log4j.Logger;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.server.TServer;
+import org.apache.thrift.server.TThreadPoolServer;
+import org.apache.thrift.transport.TServerSocket;
+import org.apache.thrift.transport.TServerTransport;
+import org.apache.thrift.transport.TTransportException;
+import org.openslx.imagemaster.thrift.iface.ImageServer;
+import org.apache.thrift.server.TThreadPoolServer.Args;
+
+public class BinaryListener implements Runnable
+{
+ private static Logger log = Logger.getLogger( BinaryListener.class );
+
+ @Override
+ public void run()
+ {
+ final ImageServerHandler handler = new ImageServerHandler();
+ final ImageServer.Processor<ImageServerHandler> processor = new ImageServer.Processor<ImageServerHandler>( handler );
+ final TProtocolFactory protFactory = new TBinaryProtocolSafe.Factory( true, true );
+ final TServerTransport transport;
+ try {
+ transport = new TServerSocket( 9090 );
+ } catch ( TTransportException e ) {
+ log.fatal( "Could not listen on port 9090" );
+ return;
+ }
+ TServer server = new TThreadPoolServer( new Args( transport ).protocolFactory( protFactory ).processor( processor )
+ .minWorkerThreads( 4 ).maxWorkerThreads( 8 ) );
+ log.info( "Starting Binary Thrift" );
+ server.serve();
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/ImageServerHandler.java b/src/main/java/org/openslx/imagemaster/thrift/server/ImageServerHandler.java
new file mode 100644
index 0000000..6be5d40
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/thrift/server/ImageServerHandler.java
@@ -0,0 +1,35 @@
+package org.openslx.imagemaster.thrift.server;
+
+import org.apache.thrift.TException;
+import org.openslx.imagemaster.server.ApiServer;
+import org.openslx.imagemaster.thrift.iface.AuthenticationException;
+import org.openslx.imagemaster.thrift.iface.ImageServer;
+import org.openslx.imagemaster.thrift.iface.InvalidTokenException;
+import org.openslx.imagemaster.thrift.iface.SessionData;
+import org.openslx.imagemaster.thrift.iface.UserInfo;
+
+public class ImageServerHandler implements ImageServer.Iface
+{
+
+ @Override
+ public boolean ping() throws TException
+ {
+ // TODO: Return false if service unavailable but running
+ 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 );
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/thrift/server/TBinaryProtocolSafe.java b/src/main/java/org/openslx/imagemaster/thrift/server/TBinaryProtocolSafe.java
new file mode 100644
index 0000000..614be22
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/thrift/server/TBinaryProtocolSafe.java
@@ -0,0 +1,121 @@
+package org.openslx.imagemaster.thrift.server;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+
+import org.apache.thrift.TException;
+import org.apache.thrift.protocol.TBinaryProtocol;
+import org.apache.thrift.protocol.TMessage;
+import org.apache.thrift.protocol.TProtocol;
+import org.apache.thrift.protocol.TProtocolException;
+import org.apache.thrift.protocol.TProtocolFactory;
+import org.apache.thrift.transport.TTransport;
+
+/**
+ * Binary protocol implementation for thrift.
+ * Will not read messages bigger than 12MiB.
+ *
+ */
+public class TBinaryProtocolSafe extends TBinaryProtocol
+{
+
+ /**
+ * Factory
+ */
+ public static class Factory implements TProtocolFactory
+ {
+ protected boolean strictRead_ = false;
+ protected boolean strictWrite_ = true;
+
+ public Factory()
+ {
+ this( false, true );
+ }
+
+ public Factory(boolean strictRead, boolean strictWrite)
+ {
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ public TProtocol getProtocol( TTransport trans )
+ {
+ return new TBinaryProtocolSafe( trans, strictRead_, strictWrite_ );
+ }
+ }
+
+ private static final int maxLen = 12 * 1024 * 1024; // 12 MiB
+
+ /**
+ * Constructor
+ */
+ public TBinaryProtocolSafe(TTransport trans)
+ {
+ this( trans, false, true );
+ }
+
+ public TBinaryProtocolSafe(TTransport trans, boolean strictRead, boolean strictWrite)
+ {
+ super( trans );
+ strictRead_ = strictRead;
+ strictWrite_ = strictWrite;
+ }
+
+ /**
+ * Reading methods.
+ */
+
+ public TMessage readMessageBegin() throws TException
+ {
+ int size = readI32();
+ if ( size > maxLen )
+ throw new TProtocolException( TProtocolException.SIZE_LIMIT, "Payload too big." );
+ if ( size < 0 ) {
+ int version = size & VERSION_MASK;
+ if ( version != VERSION_1 ) {
+ throw new TProtocolException( TProtocolException.BAD_VERSION, "Bad version in readMessageBegin" );
+ }
+ return new TMessage( readString(), (byte)( size & 0x000000ff ), readI32() );
+ } else {
+ if ( strictRead_ ) {
+ throw new TProtocolException( TProtocolException.BAD_VERSION, "Missing version in readMessageBegin, old client?" );
+ }
+ return new TMessage( readStringBody( size ), readByte(), readI32() );
+ }
+ }
+
+ public String readString() throws TException
+ {
+ int size = readI32();
+ if ( size > maxLen )
+ throw new TProtocolException( TProtocolException.SIZE_LIMIT, "Payload too big." );
+ if ( trans_.getBytesRemainingInBuffer() >= size ) {
+ try {
+ String s = new String( trans_.getBuffer(), trans_.getBufferPosition(), size, "UTF-8" );
+ trans_.consumeBuffer( size );
+ return s;
+ } catch ( UnsupportedEncodingException e ) {
+ throw new TException( "JVM DOES NOT SUPPORT UTF-8" );
+ }
+ }
+
+ return readStringBody( size );
+ }
+
+ public ByteBuffer readBinary() throws TException
+ {
+ int size = readI32();
+ if ( size > maxLen )
+ throw new TProtocolException( TProtocolException.SIZE_LIMIT, "Payload too big." );
+ if ( trans_.getBytesRemainingInBuffer() >= size ) {
+ ByteBuffer bb = ByteBuffer.wrap( trans_.getBuffer(), trans_.getBufferPosition(), size );
+ trans_.consumeBuffer( size );
+ return bb;
+ }
+
+ byte[] buf = new byte[size];
+ trans_.readAll( buf, 0, size );
+ return ByteBuffer.wrap( buf );
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/util/Hash.java b/src/main/java/org/openslx/imagemaster/util/Hash.java
new file mode 100644
index 0000000..8ac0e5f
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/util/Hash.java
@@ -0,0 +1,100 @@
+package org.openslx.imagemaster.util;
+
+import java.security.MessageDigest;
+import java.nio.charset.Charset;
+import java.security.NoSuchAlgorithmException;
+
+public class Hash
+{
+ // Cache of md5 digesters
+ private static final ThreadLocal<MessageDigest> md5hash = new ThreadLocal<MessageDigest>() {
+ @Override
+ public MessageDigest initialValue()
+ {
+ try {
+ return MessageDigest.getInstance( "MD5" );
+ } catch ( NoSuchAlgorithmException e ) {
+ e.printStackTrace();
+ System.exit(1);
+ return null;
+ }
+ }
+ };
+ // Cache of sha256 digesters
+ private static final ThreadLocal<MessageDigest> sha256hash = new ThreadLocal<MessageDigest>() {
+ @Override
+ public MessageDigest initialValue()
+ {
+ try {
+ return MessageDigest.getInstance( "SHA-256" );
+ } catch ( NoSuchAlgorithmException e ) {
+ e.printStackTrace();
+ System.exit(1);
+ return null;
+ }
+ }
+ };
+ // Cache of sha512 digesters
+ private static final ThreadLocal<MessageDigest> sha512hash = new ThreadLocal<MessageDigest>() {
+ @Override
+ public MessageDigest initialValue()
+ {
+ try {
+ return MessageDigest.getInstance( "SHA-512" );
+ } catch ( NoSuchAlgorithmException e ) {
+ e.printStackTrace();
+ System.exit(1);
+ return null;
+ }
+ }
+ };
+ // For converting to hex string
+ private static final char[] HEX_CHARS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ // Constant
+ private static final Charset UTF8 = Charset.forName( "UTF-8" );
+
+ // MD5
+
+ public static String md5( final byte[] bytes )
+ {
+ return toHexString( md5hash.get().digest( bytes ) );
+ }
+
+ public static String md5( final String text )
+ {
+ return md5( text.getBytes( UTF8 ));
+ }
+
+ // SHA-256
+
+ public static String sha256( final byte[] bytes )
+ {
+ return toHexString( sha256hash.get().digest( bytes ) );
+ }
+
+ public static String sha256( final String text )
+ {
+ return sha256( text.getBytes( UTF8 ));
+ }
+
+ // SHA-512
+
+ public static MessageDigest getSha512Digest()
+ {
+ return sha512hash.get();
+ }
+
+ // Helper
+
+ private static String toHexString( final byte[] bytes )
+ {
+ final char[] hexChars = new char[bytes.length * 2];
+ for ( int j = 0; j < bytes.length; ++j ) {
+ final int v = bytes[j] & 0xFF;
+ hexChars[j * 2] = HEX_CHARS[v >>> 4];
+ hexChars[j * 2 + 1] = HEX_CHARS[v & 0x0F];
+ }
+ return new String( hexChars );
+ }
+
+}
diff --git a/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java
new file mode 100644
index 0000000..472ead9
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/util/Sha512Crypt.java
@@ -0,0 +1,492 @@
+/*
+ Sha512Crypt.java
+
+ Created: 18 December 2007
+
+ Java Port By: James Ratcliff, falazar@arlut.utexas.edu
+
+ This class implements the new generation, scalable, SHA512-based
+ Unix 'crypt' algorithm developed by a group of engineers from Red
+ Hat, Sun, IBM, and HP for common use in the Unix and Linux
+ /etc/shadow files.
+
+ The Linux glibc library (starting at version 2.7) includes support
+ for validating passwords hashed using this algorithm.
+
+ The algorithm itself was released into the Public Domain by Ulrich
+ Drepper <drepper@redhat.com>. A discussion of the rationale and
+ development of this algorithm is at
+
+ http://people.redhat.com/drepper/sha-crypt.html
+
+ and the specification and a sample C language implementation is at
+
+ http://people.redhat.com/drepper/SHA-crypt.txt
+
+ This Java Port is
+
+ Copyright (c) 2008-2013 The University of Texas at Austin.
+
+ All rights reserved.
+
+ Redistribution and use in source and binary form are permitted
+ provided that distributions retain this entire copyright notice
+ and comment. Neither the name of the University nor the names of
+ its contributors may be used to endorse or promote products
+ derived from this software without specific prior written
+ permission. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY
+ EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ PARTICULAR PURPOSE.
+
+*/
+
+package org.openslx.imagemaster.util;
+
+import java.security.MessageDigest;
+
+/*------------------------------------------------------------------------------
+ class
+ Sha512Crypt
+
+------------------------------------------------------------------------------*/
+
+/**
+ * <p>This class defines a method, {@link
+ * Sha512Crypt#Sha512_crypt(java.lang.String, java.lang.String, int)
+ * Sha512_crypt()}, which takes a password and a salt string and
+ * generates a Sha512 encrypted password entry.</p>
+ *
+ * <p>This class implements the new generation, scalable, SHA512-based
+ * Unix 'crypt' algorithm developed by a group of engineers from Red
+ * Hat, Sun, IBM, and HP for common use in the Unix and Linux
+ * /etc/shadow files.</p>
+ *
+ * <p>The Linux glibc library (starting at version 2.7) includes
+ * support for validating passwords hashed using this algorithm.</p>
+ *
+ * <p>The algorithm itself was released into the Public Domain by
+ * Ulrich Drepper &lt;drepper@redhat.com&gt;. A discussion of the
+ * rationale and development of this algorithm is at</p>
+ *
+ * <p>http://people.redhat.com/drepper/sha-crypt.html</p>
+ *
+ * <p>and the specification and a sample C language implementation is
+ * at</p>
+ *
+ * <p>http://people.redhat.com/drepper/SHA-crypt.txt</p>
+ */
+
+public final class Sha512Crypt
+{
+ static private final String sha512_salt_prefix = "$6$";
+ static private final String sha512_rounds_prefix = "rounds=";
+ static private final int SALT_LEN_MAX = 16;
+ static private final int ROUNDS_DEFAULT = 5000;
+ static private final int ROUNDS_MIN = 1000;
+ static private final int ROUNDS_MAX = 999999999;
+ static private final String SALTCHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
+ static private final String itoa64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+ static private MessageDigest getSHA512()
+ {
+ try
+ {
+ return MessageDigest.getInstance("SHA-512");
+ }
+ catch (java.security.NoSuchAlgorithmException ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * <p>This method actually generates an Sha512 crypted password hash
+ * from a plaintext password and a salt.</p>
+ *
+ * <p>The resulting string will be in the form
+ * '$6$&lt;rounds=n&gt;$&lt;salt&gt;$&lt;hashed mess&gt;</p>
+ *
+ * @param keyStr Plaintext password
+ *
+ * @param saltStr An encoded salt/roundes which will be consulted to determine the salt
+ * and round count, if not null
+ *
+ * @param roundsCount If this value is not 0, this many rounds will
+ * used to generate the hash text.
+ *
+ * @return The Sha512 Unix Crypt hash text for the keyStr
+ */
+
+ public static final String Sha512_crypt(String keyStr, String saltStr, int roundsCount)
+ {
+ MessageDigest ctx = getSHA512();
+ MessageDigest alt_ctx = getSHA512();
+
+ byte[] alt_result;
+ byte[] temp_result;
+ byte[] p_bytes = null;
+ byte[] s_bytes = null;
+ int cnt, cnt2;
+ int rounds = ROUNDS_DEFAULT; // Default number of rounds.
+ StringBuilder buffer;
+ boolean include_round_count = false;
+
+ /* -- */
+
+ if (saltStr != null)
+ {
+ if (saltStr.startsWith(sha512_salt_prefix))
+ {
+ saltStr = saltStr.substring(sha512_salt_prefix.length());
+ }
+
+ if (saltStr.startsWith(sha512_rounds_prefix))
+ {
+ String num = saltStr.substring(sha512_rounds_prefix.length(), saltStr.indexOf('$'));
+ int srounds = Integer.valueOf(num).intValue();
+ saltStr = saltStr.substring(saltStr.indexOf('$')+1);
+ rounds = Math.max(ROUNDS_MIN, Math.min(srounds, ROUNDS_MAX));
+ include_round_count = true;
+ }
+
+ if (saltStr.length() > SALT_LEN_MAX)
+ {
+ saltStr = saltStr.substring(0, SALT_LEN_MAX);
+ }
+
+ // gnu libc's crypt(3) implementation allows the salt to end
+ // in $ which is then ignored.
+
+ if (saltStr.endsWith("$"))
+ {
+ saltStr = saltStr.substring(0, saltStr.length() - 1);
+ }
+ else
+ {
+ if (saltStr.indexOf("$") != -1)
+ {
+ saltStr = saltStr.substring(0, saltStr.indexOf("$"));
+ }
+ }
+ }
+ else
+ {
+ java.util.Random randgen = new java.util.Random();
+ StringBuilder saltBuf = new StringBuilder();
+
+ while (saltBuf.length() < 16)
+ {
+ int index = (int) (randgen.nextFloat() * SALTCHARS.length());
+ saltBuf.append(SALTCHARS.substring(index, index+1));
+ }
+
+ saltStr = saltBuf.toString();
+ }
+
+ if (roundsCount != 0)
+ {
+ rounds = Math.max(ROUNDS_MIN, Math.min(roundsCount, ROUNDS_MAX));
+ }
+
+ byte[] key = keyStr.getBytes();
+ byte[] salt = saltStr.getBytes();
+
+ ctx.reset();
+ ctx.update(key, 0, key.length);
+ ctx.update(salt, 0, salt.length);
+
+ alt_ctx.reset();
+ alt_ctx.update(key, 0, key.length);
+ alt_ctx.update(salt, 0, salt.length);
+ alt_ctx.update(key, 0, key.length);
+
+ alt_result = alt_ctx.digest();
+
+ for (cnt = key.length; cnt > 64; cnt -= 64)
+ {
+ ctx.update(alt_result, 0, 64);
+ }
+
+ ctx.update(alt_result, 0, cnt);
+
+ for (cnt = key.length; cnt > 0; cnt >>= 1)
+ {
+ if ((cnt & 1) != 0)
+ {
+ ctx.update(alt_result, 0, 64);
+ }
+ else
+ {
+ ctx.update(key, 0, key.length);
+ }
+ }
+
+ alt_result = ctx.digest();
+
+ alt_ctx.reset();
+
+ for (cnt = 0; cnt < key.length; ++cnt)
+ {
+ alt_ctx.update(key, 0, key.length);
+ }
+
+ temp_result = alt_ctx.digest();
+
+ p_bytes = new byte[key.length];
+
+ for (cnt2 = 0, cnt = p_bytes.length; cnt >= 64; cnt -= 64)
+ {
+ System.arraycopy(temp_result, 0, p_bytes, cnt2, 64);
+ cnt2 += 64;
+ }
+
+ System.arraycopy(temp_result, 0, p_bytes, cnt2, cnt);
+
+ alt_ctx.reset();
+
+ for (cnt = 0; cnt < 16 + (alt_result[0]&0xFF); ++cnt)
+ {
+ alt_ctx.update(salt, 0, salt.length);
+ }
+
+ temp_result = alt_ctx.digest();
+
+ s_bytes = new byte[salt.length];
+
+ for (cnt2 = 0, cnt = s_bytes.length; cnt >= 64; cnt -= 64)
+ {
+ System.arraycopy(temp_result, 0, s_bytes, cnt2, 64);
+ cnt2 += 64;
+ }
+
+ System.arraycopy(temp_result, 0, s_bytes, cnt2, cnt);
+
+ /* Repeatedly run the collected hash value through SHA512 to burn
+ CPU cycles. */
+
+ for (cnt = 0; cnt < rounds; ++cnt)
+ {
+ ctx.reset();
+
+ if ((cnt & 1) != 0)
+ {
+ ctx.update(p_bytes, 0, key.length);
+ }
+ else
+ {
+ ctx.update (alt_result, 0, 64);
+ }
+
+ if (cnt % 3 != 0)
+ {
+ ctx.update(s_bytes, 0, salt.length);
+ }
+
+ if (cnt % 7 != 0)
+ {
+ ctx.update(p_bytes, 0, key.length);
+ }
+
+ if ((cnt & 1) != 0)
+ {
+ ctx.update(alt_result, 0, 64);
+ }
+ else
+ {
+ ctx.update(p_bytes, 0, key.length);
+ }
+
+ alt_result = ctx.digest();
+ }
+
+ buffer = new StringBuilder(sha512_salt_prefix);
+
+ if (include_round_count || rounds != ROUNDS_DEFAULT)
+ {
+ buffer.append(sha512_rounds_prefix);
+ buffer.append(rounds);
+ buffer.append("$");
+ }
+
+ buffer.append(saltStr);
+ buffer.append("$");
+
+ buffer.append(b64_from_24bit (alt_result[0], alt_result[21], alt_result[42], 4));
+ buffer.append(b64_from_24bit (alt_result[22], alt_result[43], alt_result[1], 4));
+ buffer.append(b64_from_24bit (alt_result[44], alt_result[2], alt_result[23], 4));
+ buffer.append(b64_from_24bit (alt_result[3], alt_result[24], alt_result[45], 4));
+ buffer.append(b64_from_24bit (alt_result[25], alt_result[46], alt_result[4], 4));
+ buffer.append(b64_from_24bit (alt_result[47], alt_result[5], alt_result[26], 4));
+ buffer.append(b64_from_24bit (alt_result[6], alt_result[27], alt_result[48], 4));
+ buffer.append(b64_from_24bit (alt_result[28], alt_result[49], alt_result[7], 4));
+ buffer.append(b64_from_24bit (alt_result[50], alt_result[8], alt_result[29], 4));
+ buffer.append(b64_from_24bit (alt_result[9], alt_result[30], alt_result[51], 4));
+ buffer.append(b64_from_24bit (alt_result[31], alt_result[52], alt_result[10], 4));
+ buffer.append(b64_from_24bit (alt_result[53], alt_result[11], alt_result[32], 4));
+ buffer.append(b64_from_24bit (alt_result[12], alt_result[33], alt_result[54], 4));
+ buffer.append(b64_from_24bit (alt_result[34], alt_result[55], alt_result[13], 4));
+ buffer.append(b64_from_24bit (alt_result[56], alt_result[14], alt_result[35], 4));
+ buffer.append(b64_from_24bit (alt_result[15], alt_result[36], alt_result[57], 4));
+ buffer.append(b64_from_24bit (alt_result[37], alt_result[58], alt_result[16], 4));
+ buffer.append(b64_from_24bit (alt_result[59], alt_result[17], alt_result[38], 4));
+ buffer.append(b64_from_24bit (alt_result[18], alt_result[39], alt_result[60], 4));
+ buffer.append(b64_from_24bit (alt_result[40], alt_result[61], alt_result[19], 4));
+ buffer.append(b64_from_24bit (alt_result[62], alt_result[20], alt_result[41], 4));
+ buffer.append(b64_from_24bit ((byte)0x00, (byte)0x00, alt_result[63], 2));
+
+ /* Clear the buffer for the intermediate result so that people
+ attaching to processes or reading core dumps cannot get any
+ information. */
+
+ ctx.reset();
+
+ return buffer.toString();
+ }
+
+ private static final String b64_from_24bit(byte B2, byte B1, byte B0, int size)
+ {
+ int v = ((((int) B2) & 0xFF) << 16) | ((((int) B1) & 0xFF) << 8) | ((int)B0 & 0xff);
+
+ StringBuilder result = new StringBuilder();
+
+ while (--size >= 0)
+ {
+ result.append(itoa64.charAt((int) (v & 0x3f)));
+ v >>>= 6;
+ }
+
+ return result.toString();
+ }
+
+ /**
+ * <p>This method tests a plaintext password against a SHA512 Unix
+ * Crypt'ed hash and returns true if the password matches the
+ * hash.</p>
+ *
+ * @param plaintextPass The plaintext password text to test.
+ * @param sha512CryptText The hash text we're testing against.
+ * We'll extract the salt and the round count from this String.
+ */
+
+ static public final boolean verifyPassword(String plaintextPass, String sha512CryptText)
+ {
+ if (sha512CryptText.startsWith("$6$"))
+ {
+ return sha512CryptText.equals(Sha512_crypt(plaintextPass, sha512CryptText, 0));
+ }
+ else
+ {
+ throw new RuntimeException("Bad sha512CryptText");
+ }
+ }
+
+ /**
+ * <p>Returns true if sha512CryptText is a valid Sha512Crypt hashtext,
+ * false if not.</p>
+ */
+
+ public static final boolean verifyHashTextFormat(String sha512CryptText)
+ {
+ if (!sha512CryptText.startsWith(sha512_salt_prefix))
+ {
+ return false;
+ }
+
+ sha512CryptText = sha512CryptText.substring(sha512_salt_prefix.length());
+
+ if (sha512CryptText.startsWith(sha512_rounds_prefix))
+ {
+ String num = sha512CryptText.substring(sha512_rounds_prefix.length(), sha512CryptText.indexOf('$'));
+
+ try
+ {
+ int srounds = Integer.valueOf(num).intValue();
+ }
+ catch (NumberFormatException ex)
+ {
+ return false;
+ }
+
+ sha512CryptText = sha512CryptText.substring(sha512CryptText.indexOf('$')+1);
+ }
+
+ if (sha512CryptText.indexOf('$') > (SALT_LEN_MAX + 1))
+ {
+ return false;
+ }
+
+ sha512CryptText = sha512CryptText.substring(sha512CryptText.indexOf('$') + 1);
+
+ for (int i = 0; i < sha512CryptText.length(); i++)
+ {
+ if (itoa64.indexOf(sha512CryptText.charAt(i)) == -1)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * <p>Validate our implementation using test data from Ulrich
+ * Drepper's C implementation.</p>
+ */
+
+ private static void selfTest()
+ {
+ String msgs[] =
+ {
+ "$6$saltstring", "Hello world!", "$6$saltstring$svn8UoSVapNtMuq1ukKS4tPQd8iKwSMHWjl/O817G3uBnIFNjnQJuesI68u4OTLiBFdcbYEdFCoEOfaS35inz1",
+ "$6$xxxxxxxx", "geheim", "$6$xxxxxxxx$wuSdyeOvQXjj/nNoWnjjo.6OxUWrQFRIj019kh1cDpun6l6cpr3ywSrBprYRYZXcm4Kv9lboCEFI3GzBkdNAz/",
+ "$6$rounds=10000$saltstringsaltstring", "Hello world!", "$6$rounds=10000$saltstringsaltst$OW1/O6BYHV6BcXZu8QVeXbDWra3Oeqh0sbHbbMCVNSnCM/UrjmM0Dp8vOuZeHBy/YTBmSK6H9qs/y3RnOaw5v.",
+ "$6$rounds=5000$toolongsaltstring", "This is just a test", "$6$rounds=5000$toolongsaltstrin$lQ8jolhgVRVhY4b5pZKaysCLi0QBxGoNeKQzQ3glMhwllF7oGDZxUhx1yxdYcz/e1JSbq3y6JMxxl8audkUEm0",
+ "$6$rounds=1400$anotherlongsaltstring", "a very much longer text to encrypt. This one even stretches over morethan one line.", "$6$rounds=1400$anotherlongsalts$POfYwTEok97VWcjxIiSOjiykti.o/pQs.wPvMxQ6Fm7I6IoYN3CmLs66x9t0oSwbtEW7o7UmJEiDwGqd8p4ur1",
+ "$6$rounds=77777$short", "we have a short salt string but not a short password", "$6$rounds=77777$short$WuQyW2YR.hBNpjjRhpYD/ifIw05xdfeEyQoMxIXbkvr0gge1a1x3yRULJ5CCaUeOxFmtlcGZelFl5CxtgfiAc0",
+ "$6$rounds=123456$asaltof16chars..", "a short string", "$6$rounds=123456$asaltof16chars..$BtCwjqMJGx5hrJhZywWvt0RLE8uZ4oPwcelCjmw2kSYu.Ec6ycULevoBK25fs2xXgMNrCzIMVcgEJAstJeonj1",
+ "$6$rounds=10$roundstoolow", "the minimum number is still observed", "$6$rounds=1000$roundstoolow$kUMsbe306n21p9R.FRkW3IGn.S9NPN0x50YhH1xhLsPuWGsUSklZt58jaTfF4ZEQpyUNGc0dqbpBYYBaHHrsX.",
+ };
+
+ System.out.println("Starting Sha512Crypt tests now...");
+
+ for (int t=0; t<(msgs.length/3); t++)
+ {
+ String saltPrefix = msgs[t*3];
+ String plainText = msgs[t*3+1];
+ String cryptText = msgs[t*3+2];
+
+ String result = Sha512_crypt(plainText, cryptText, 0);
+
+ System.out.println("test " + t + " result is:" + result);
+ System.out.println("test " + t + " should be:" + cryptText);
+
+ if (result.equals(cryptText))
+ {
+ System.out.println("Passed Crypt well");
+ }
+ else
+ {
+ System.out.println("Failed Crypt Badly");
+ }
+
+ if (verifyPassword(plainText, cryptText))
+ {
+ System.out.println("Passed verifyPassword well");
+ }
+ else
+ {
+ System.out.println("Failed verifyPassword Badly");
+ }
+ }
+ }
+
+ /**
+ * Test rig
+ */
+
+ public static void main(String arg[])
+ {
+ selfTest();
+ }
+}
diff --git a/src/main/java/org/openslx/imagemaster/util/Util.java b/src/main/java/org/openslx/imagemaster/util/Util.java
new file mode 100644
index 0000000..ff0b8c1
--- /dev/null
+++ b/src/main/java/org/openslx/imagemaster/util/Util.java
@@ -0,0 +1,16 @@
+package org.openslx.imagemaster.util;
+
+public class Util
+{
+
+ public static void notNullFatal( Object something, String message )
+ {
+ if ( something == null ) {
+ if ( message != null )
+ System.out.println( "[NOTNULL] " + message );
+ System.out.println( Thread.currentThread().getStackTrace().toString() );
+ System.exit( 2 );
+ }
+ }
+
+}
diff --git a/src/main/thrift/imagemaster.thrift b/src/main/thrift/imagemaster.thrift
new file mode 100644
index 0000000..7c59079
--- /dev/null
+++ b/src/main/thrift/imagemaster.thrift
@@ -0,0 +1,59 @@
+/**
+ * Define some namespace/package name for our stuff
+ */
+
+namespace java org.openslx.imagemaster.thrift.iface
+namespace php testing
+
+typedef string ID
+typedef string Token
+
+enum AuthorizationError {
+ GENERIC_ERROR,
+ NOT_AUTHENTICATED,
+ NO_PERMISSION
+}
+
+enum AuthenticationError {
+ GENERIC_ERROR,
+ INVALID_CREDENTIALS,
+ ACCOUNT_SUSPENDED,
+ BANNED_NETWORK
+}
+
+exception AuthorizationException {
+ 1: AuthorizationError number,
+ 2: string message
+}
+
+exception AuthenticationException {
+ 1: AuthenticationError number,
+ 2: string message
+}
+
+exception InvalidTokenException {
+}
+
+struct UserInfo {
+ 1: string userId,
+ 2: string firstName,
+ 3: string lastName,
+ 4: string eMail
+}
+
+struct SessionData {
+ 1: ID sessionId,
+ 2: Token authToken,
+ 3: string serverAddress
+}
+
+service ImageServer {
+
+ bool ping(),
+
+ SessionData authenticate(1:string username, 2:string password) throws (1:AuthenticationException failure),
+
+ UserInfo getUserFromToken(1:Token token) throws (1:InvalidTokenException failure)
+
+}
+
diff --git a/src/test/java/org/openslx/imagemaster/AppTest.java b/src/test/java/org/openslx/imagemaster/AppTest.java
new file mode 100644
index 0000000..38cff57
--- /dev/null
+++ b/src/test/java/org/openslx/imagemaster/AppTest.java
@@ -0,0 +1,38 @@
+package org.openslx.imagemaster;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Unit test for simple App.
+ */
+public class AppTest
+ extends TestCase
+{
+ /**
+ * Create the test case
+ *
+ * @param testName name of the test case
+ */
+ public AppTest( String testName )
+ {
+ super( testName );
+ }
+
+ /**
+ * @return the suite of tests being tested
+ */
+ public static Test suite()
+ {
+ return new TestSuite( AppTest.class );
+ }
+
+ /**
+ * Rigourous Test :-)
+ */
+ public void testApp()
+ {
+ assertTrue( true );
+ }
+}
diff --git a/thrift-compile.sh b/thrift-compile.sh
new file mode 100755
index 0000000..0a1099b
--- /dev/null
+++ b/thrift-compile.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+rm -r gen-java
+thrift --gen java src/main/thrift/imagemaster.thrift && \
+ rm -r src/main/java/org/openslx/imagemaster/thrift/iface && \
+ cp -r gen-java/org src/main/java/
+