Commit 07b4af6f authored by Mat's avatar Mat

Create CLI client

commons-cli argument handling
Add verbose option
User authentication and creation customizable
Use checkAuthBackend
Better error handling & messages
Repository setup and search parametrized
Serialize the client state and store it into a file
Parametrize update operation
Remove debug messages
Fix search word case sensitivity
Parametrized backend selection
Trim search results from dynrh2lev(rocks)
Make the repository password changable in Client
parent 1307e0cb
package searchitect.client.dynrh2levplugin;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
......@@ -13,6 +16,8 @@ import java.util.concurrent.ExecutionException;
import javax.crypto.NoSuchPaddingException;
import org.apache.commons.io.output.ByteArrayOutputStream;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Multimap;
......@@ -138,8 +143,30 @@ public class Clientdynrh2levImpl implements ClientScheme{
e.printStackTrace();
throw new SearchitectException(e.getMessage());
}
}
@Override
public byte[] serializeState() {
if(state == null)
return null;
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
new ObjectOutputStream(out).writeObject(this.state);
return out.toByteArray();
}catch (Exception e) {
return null;
}
}
@Override
public void deserializeState(byte[] state) throws SearchitectException{
if(state == null)
this.state = null;
try (ByteArrayInputStream in = new ByteArrayInputStream(state)) {
this.state = (ClientStatedynrh2lev)(new ObjectInputStream(in).readObject());
RH2LevModifiedMap.master = this.state.getSk();
}catch (Exception e) {
throw new SearchitectException("Could not deserialize state", e);
}
}
......
......@@ -2,7 +2,10 @@ package searchitect.client.dynrh2levplugin;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
......@@ -15,11 +18,14 @@ import java.util.concurrent.ExecutionException;
import javax.crypto.NoSuchPaddingException;
import org.apache.commons.io.output.ByteArrayOutputStream;
import searchitect.common.viewdynrh2lev.SearchTokendynrh2lev;
import searchitect.common.viewdynrh2lev.UpdateIndexdynrh2lev;
import searchitect.common.viewdynrh2lev.UploadIndexdynrh2levMap;
import searchitect.clusion.CryptoPrimitivesModified;
import searchitect.clusion.DynRH2LevModifiedRocks;
import searchitect.clusion.RH2LevModifiedMap;
import searchitect.clusion.RH2LevModifiedRocks;
import com.fasterxml.jackson.core.JsonProcessingException;
......@@ -143,8 +149,30 @@ public class Clientdynrh2levrocksImpl implements ClientScheme{
e.printStackTrace();
throw new SearchitectException(e.getMessage());
}
}
@Override
public byte[] serializeState() {
if(state == null)
return null;
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
new ObjectOutputStream(out).writeObject(this.state);
return out.toByteArray();
}catch (Exception e) {
return null;
}
}
@Override
public void deserializeState(byte[] state) throws SearchitectException{
if(state == null)
this.state = null;
try (ByteArrayInputStream in = new ByteArrayInputStream(state)) {
this.state = (ClientStatedynrh2levrocks)(new ObjectInputStream(in).readObject());
RH2LevModifiedRocks.master = this.state.getSk();
}catch (Exception e) {
throw new SearchitectException("Could not deserialize state", e);
}
}
}
package searchitect.client.sophos;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.UnsupportedEncodingException;
import java.security.KeyException;
......@@ -11,6 +14,8 @@ import java.security.spec.InvalidKeySpecException;
import java.security.spec.InvalidParameterSpecException;
import java.util.HashMap;
import org.apache.commons.io.output.ByteArrayOutputStream;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Multimap;
......@@ -85,6 +90,8 @@ public class ClientSophosImpl implements ClientScheme {
* @see searchitect.scheme.IClientScheme#search(java.lang.String)
*/
public String search(String query) throws SearchitectException {
if(state == null)
throw new SearchitectException("Client state has not been initialized");
try {
// generate search token
SearchTokenSophos token = Sophos.token(query, state);
......@@ -125,6 +132,26 @@ public class ClientSophosImpl implements ClientScheme {
}
@Override
public byte[] serializeState() {
if(state == null)
return null;
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
new ObjectOutputStream(out).writeObject(this.state);
return out.toByteArray();
}catch (Exception e) {
return null;
}
}
@Override
public void deserializeState(byte[] state) throws SearchitectException{
if(state == null)
this.state = null;
try (ByteArrayInputStream in = new ByteArrayInputStream(state)) {
this.state = (ClientStateSophos)(new ObjectInputStream(in).readObject());
}catch (Exception e) {
throw new SearchitectException("Could not deserialize state", e);
}
}
}
......@@ -150,6 +150,12 @@ public class ClientSophosImplTest {
assertEquals(sophos.getImplName(),"sophos");
}
@Test(expected = SearchitectException.class)
public void searchWithoutStateTest(){
ClientSophosImpl sophos = new ClientSophosImpl();
sophos.search("foo");
}
@Test
public void setupKeywordSearchSuccessTest() throws KeyException, NoSuchAlgorithmException, InvalidKeySpecException, FactoriesException, InvalidAlgorithmParameterException, NoSuchProviderException, NoSuchPaddingException, IOException {
......
8@rq Y
\ No newline at end of file
package searchitect;
import org.apache.commons.cli.*;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.HttpClientErrorException;
import searchitect.client.Client;
import searchitect.client.DocumentIndexer;
import searchitect.client.dynrh2levplugin.Clientdynrh2levImpl;
import searchitect.client.dynrh2levplugin.Clientdynrh2levrocksImpl;
import searchitect.client.sophos.ClientSophosImpl;
import searchitect.common.client.ClientScheme;
import searchitect.common.exception.SearchitectException;
import searchitect.common.view.RepositoryInfo;
import searchitect.common.view.SearchResult;
import searchitect.common.view.UserAuthRequest;
......@@ -17,125 +21,311 @@ import searchitect.common.view.UserAuthResponse;
@SpringBootApplication
public class ClientRunner {
private static Options cliOptions;
static {
cliOptions = new Options();
cliOptions.addOption("s", "serverurl", true, "URL of a Searchitect Gate");
cliOptions.addOption("u", "username", true, "Username on the Searchitect Gate");
cliOptions.addOption("p", "password", true, "Password on the Searchitect Gate");
cliOptions.addOption("P", "repository-password", true, "Password for the encryption of the search index. Must be given when a repository is created.");
cliOptions.addOption("c", "createuser", false, "Create a new user on the Searchitect Gate");
cliOptions.addOption("d", "path", true, "Path to the document folder");
cliOptions.addOption("U", "update", true, "Path to the document folder for an update operation");
cliOptions.addOption("r", "repository", true, "Name of the SE repository");
cliOptions.addOption("q", "search", true, "Search query string");
cliOptions.addOption("S", "state", true, "File where the client state is stored");
cliOptions.addOption("b", "backend", true, "The backend to use. Allowed values: sophos (default), dynrh2lev, dynrh2levrocks");
cliOptions.addOption("v", "verbose", false, "Enable verbose output");
cliOptions.addOption("h", "help", true, "Print help");
}
public static void printHelpAndExit() {
printHelpAndExit(-1);
}
/**
* Prints the help message to stdout and exits the program
* @param status The exit status of the programm. Defaults to -1
*/
public static void printHelpAndExit(int status) {
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "searchitect-client", cliOptions );
System.exit(status);
}
public static void main(String[] args) {
SpringApplication.run(ClientRunner.class, args);
System.out.println("welcome to searchitect");
final String uri = "https://localhost:8433/";
String setuppath = "/home/nan/Downloads/maildir/allen-p/";
String updatepath = "../update";
String username ="xyz";
String query= "first";
String uri = "https://localhost:8433/";
String setuppath = null;
String updatepath = null;
String username = null;
String password = null;
String repositoryPassword = null;
String searchString = "";
String repositoryName = "";
String stateFile = null;
boolean verbose = false;
boolean createUser = false;
ClientScheme clientimpl = null;
// Parse the commandline input
try {
CommandLineParser cliParser = new DefaultParser();
CommandLine cmd = cliParser.parse( cliOptions, args);
if(cmd.hasOption("help")) {
printHelpAndExit(0);
}
if(cmd.hasOption("verbose"))
verbose = true;
if(cmd.hasOption("createuser"))
createUser = true;
if(cmd.hasOption("serverurl")) {
uri = cmd.getOptionValue("serverurl");
System.out.println("URL set to "+uri);
}else
if(verbose)
System.out.println("No server URL specified. Using the default value " + uri);
if(cmd.hasOption("username")) {
username = cmd.getOptionValue("username");
if(verbose)
System.out.println("Using the username " + username);
}else {
//Required option
System.err.println("No username specified.");
printHelpAndExit();
}
if(cmd.hasOption("password")) {
password = cmd.getOptionValue("password").trim();
}
if(password == null || password == "") {
//Read password from the console
char[] pwEntered = System.console().readPassword("Please enter the password for user " + username + ":");
password = new String(pwEntered).trim();
}
if(cmd.hasOption("path")) {
setuppath = cmd.getOptionValue("path");
}
if(cmd.hasOption("repository-password")) {
repositoryPassword = cmd.getOptionValue("repository-password").trim();
}
if(setuppath != null) {
// We need a repository-password if we setup a new repository
if(repositoryPassword == null || repositoryPassword == "") {
//Read password from the console
char[] pwEntered = System.console().readPassword("Please enter the password for the new repository:");
repositoryPassword = new String(pwEntered).trim();
}
}
if(cmd.hasOption("update")) {
updatepath = cmd.getOptionValue("update");
}
if(cmd.hasOption("search")) {
searchString = cmd.getOptionValue("search").toLowerCase();
}
if(cmd.hasOption("repository")) {
repositoryName = cmd.getOptionValue("repository");
}
if(cmd.hasOption("state")) {
stateFile = cmd.getOptionValue("state");
}
if(cmd.hasOption("backend")) {
switch(cmd.getOptionValue("backend").toLowerCase()) {
case "sophos":
clientimpl = new ClientSophosImpl();
break;
case "dynrh2lev":
clientimpl = new Clientdynrh2levImpl();
break;
case "dynrh2levrocks":
clientimpl = new Clientdynrh2levrocksImpl();
break;
default:
System.err.println("Unknown backend specified.");
printHelpAndExit();
break;
}
} else
//Set the default backend if none is specified
clientimpl = new ClientSophosImpl();
}catch (Exception e) {
System.err.println("Illegal commandline option used.");
HelpFormatter formatter = new HelpFormatter();
formatter.printHelp( "searchitect-client", cliOptions );
return;
}
DocumentIndexer parser = DocumentIndexer.parseDirectory(setuppath);
//Disable Spring logging
System.setProperty("logging.level.org.springframework", "OFF");
System.setProperty("logging.level.root", "OFF");
System.setProperty("spring.main.banner-mode", "off");
SpringApplication.run(ClientRunner.class, args);
if(verbose)
System.out.println("welcome to Searchitect!");
try {
// ClientScheme clientimpl = (ClientScheme) new Clientdynrh2levImpl();
ClientScheme clientimpl = (ClientScheme) new ClientSophosImpl();
Client client = new Client(uri,clientimpl);
System.out.println(client.check());
//System.out.println(client.checkBackend());
try {
System.out.println("create user\n");
ResponseEntity<String> string = client.createUser(new UserAuthRequest(username, "notsecurepassword"));
System.out.println(string.getStatusCodeValue());
System.out.println("authenticate user\n");
}
catch(Exception e) {
System.out.println("user exists!! \n");
}
UserAuthResponse token = client.authenticateUser(new UserAuthRequest(username, "notsecurepassword"));
System.out.println(token.getToken());
System.out.println("Check authentication and backend communication: \n");
System.out.println(client.checkAuthBackend());
System.out.println("Setup: \n");
while(parser.isFinished()==false){
System.out.println("...is Parsing");
}
RepositoryInfo repo = client.setup("testpwd", parser.getInvertedIndex(),parser.getSizeOfDocuments());
System.out.println("Repository has been initialized: \n" + repo.getrepositoryName());
System.out.println("1. Search: \n");
SearchResult result1 = client.search(repo.getrepositoryName(), query);
System.out.println("Searchresult1: \n" + result1.getResultList().toString());
parser = DocumentIndexer.parseDirectory(updatepath);
System.out.println("Update: \n");
ResponseEntity<String> up = client.update(repo.getrepositoryName(), parser.getInvertedIndex(), username);
System.out.println("Update result: \n" + up.getStatusCodeValue());
System.out.println("2. Search: \n");
SearchResult result2 =client.search(repo.getrepositoryName(), query);
System.out.println("Searchresult2: \n" + result2.getResultList().toString());
System.out.println("2. Update: \n");
ResponseEntity<String> up2 = client.update(repo.getrepositoryName(), parser.getInvertedIndex(), username);
System.out.println("Update result: \n" + up2.getStatusCodeValue());
System.out.println("3. Search: \n");
SearchResult result3 =client.search(repo.getrepositoryName(), query);
System.out.println("Searchresult3: \n" + result3.getResultList().toString());
Client client = new Client(uri,clientimpl);
//Check availability of the Gate
try {
client.check();
}catch(Exception e) {
System.err.println(String.format("Searchitect Gate at %s not available", uri));
System.exit(-1);
}
try {
//Create the user (if neccessary)
if(createUser) {
ResponseEntity<String> createUserResponse = client.createUser(new UserAuthRequest(username, password));
System.out.println(String.format("User '%s' created", username));
if(verbose)
System.out.println(String.format("Return code from the server: %s", createUserResponse.getStatusCodeValue()));
}
} catch(Exception e) {
System.err.println("Error creating the user.\n");
if(verbose)
System.err.println(e.toString());
System.exit(-1);
}
//Authenticate to the gate
UserAuthResponse token = null;
try{
token = client.authenticateUser(new UserAuthRequest(username, password));
}catch(HttpClientErrorException e) {
if(e.getStatusCode() == HttpStatus.UNAUTHORIZED) {
System.err.println("Authentication failed.");
} else {
//Unknown error. We still cannot go on
System.err.println("Unknown error during authentication with the Searchitect Gate.");
}
System.exit(-1);
}
if(verbose)
System.out.println(String.format("User '%s' authenticated.", username));
if(verbose)
System.out.println("Token: '" + token.getToken() + "'.");
//Check if the SE backend is available for the authenticated use
try {
String checkAuthBackendMsg = client.checkAuthBackend().toString();
if(verbose)
System.out.println(String.format("Welcome message from the SE backend: '%s'", checkAuthBackendMsg));
}catch(HttpClientErrorException e) {
System.err.println("The backend is currently not available for this user.");
if(verbose) System.err.println(e.toString());
System.exit(-1);
}
//Setup the repository (if necessary)
if(setuppath != null) {
// Parse the documents
DocumentIndexer parser = DocumentIndexer.parseDirectory(setuppath);
System.out.println("Setup: \n");
RepositoryInfo repo = client.setup(repositoryPassword, parser.getInvertedIndex(), parser.getSizeOfDocuments());
if(repo == null) {
System.err.println("Repository setup failed.");
System.exit(-1);
}
repositoryName = repo.getrepositoryName();
System.out.println(String.format("The new repository '%s' has been initialized.", repositoryName));
//Store the new state
if(stateFile != null) {
try {
client.writeState(stateFile);
if(verbose)
System.out.println(String.format("Client state stored to '%s'.", stateFile));
}catch(SearchitectException e) {
System.err.println("Could not store the client state.");
if(verbose)
e.printStackTrace();
}
}
}else if (stateFile != null) {
//Load the stored state
try {
client.readState(stateFile);
if(verbose)
System.out.println(String.format("Client state read from '%s'.", stateFile));
}catch(SearchitectException e) {
System.err.println(String.format("Error while reading the state from '%s'.", stateFile));
if(verbose)
e.printStackTrace();
printHelpAndExit();
}
}else {
//neither a statefile was given nor a new repository was setup.
System.err.println("No statefile was given and a new repository has not been setup either. This will likely cause an error.");
//We just print this warning and try to continue anyway
}
//Run the update operation
if(updatepath != null) {
if(verbose)
System.out.println(String.format("Updating the repository from the directory '%s'.", updatepath));
//TODO: The update path should only contain new files, otherwise we will have duplicates in the search index
try {
DocumentIndexer parser = DocumentIndexer.parseDirectory(updatepath);
ResponseEntity<String> up = client.update(repositoryName, parser.getInvertedIndex(), username);
System.out.println("Update succesful");;
} catch(SearchitectException e) {
System.err.println("Update failed.");
if(verbose)
e.printStackTrace();
System.exit(-1);
}
//Store the new state
if(stateFile != null) {
try {
client.writeState(stateFile);
if(verbose)
System.out.println(String.format("Client state stored to '%s'.", stateFile));
}catch(SearchitectException e) {
System.err.println("Could not store the client state.");
if(verbose)
e.printStackTrace();
}
}
}
//Run search
if(!repositoryName.isEmpty() && !searchString.isEmpty()) {
SearchResult result1 = client.search(repositoryName, searchString);
if(result1.getResultList().isEmpty()) {
System.out.println(String.format("The search for '%s' did not yield any results.", searchString));
} else {
System.out.println(String.format(
"Search results for the search term '%s':",
searchString));
for(String searchResult : result1.getResultList()) {
System.out.println(String.format("* %s", searchResult));
}
}
}
}
catch(Exception e){
System.out.println("creation of inverted index failed");
System.err.println("creation of inverted index failed");
if(verbose) {