working on scanner API refactoring (doesn't compile yet)

android-scanner
Tal Moran 2017-06-22 17:56:59 +03:00
parent c7dd5bd663
commit ceba09e65c
16 changed files with 218 additions and 132 deletions

View File

@ -1,20 +0,0 @@
syntax = "proto3";
package meerkat;
import "meerkat/voting.proto";
option java_package = "meerkat.protobuf";
// Container for scanned data
message ScannedData {
bytes channel = 1;
SignedEncryptedBallot signed_encrypted_ballot = 2;
}
// Container for error messages
message ErrorMsg {
string msg = 1;
}

View File

@ -1,8 +1,11 @@
package meerkat.pollingstation; package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback;
import meerkat.pollingstation.controller.PollingStationControllerInterface; import meerkat.pollingstation.controller.PollingStationControllerInterface;
import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
import meerkat.pollingstation.controller.commands.PollingStationCommand; import meerkat.pollingstation.controller.commands.PollingStationCommand;
import meerkat.pollingstation.controller.commands.ReceivedScanCommand; import meerkat.pollingstation.controller.commands.ReceivedScanCommand;
import meerkat.protobuf.PollingStation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -17,20 +20,31 @@ public class PollingStationMainController implements PollingStationControllerInt
private LinkedBlockingQueue<PollingStationCommand> queue; private LinkedBlockingQueue<PollingStationCommand> queue;
private volatile boolean shutDownHasBeenCalled; private volatile boolean shutDownHasBeenCalled;
private static ReceiverScanHandler server;
private static final String ADDRESS = "http://localhost";
private static final String SUB_ADDRESS = "";
private static final int PORT = 8080;
public PollingStationMainController() { public PollingStationMainController() {
queue = new LinkedBlockingQueue<PollingStationCommand>(); queue = new LinkedBlockingQueue<PollingStationCommand>();
shutDownHasBeenCalled = false; shutDownHasBeenCalled = false;
} }
@Override @Override
public void init(ReceiverScanHandler server) { public void init(PollingStationScanner.Consumer server) {
server.setControllerQueue(queue); server.subscribe(new FutureCallback<PollingStation.ScannedData>() {
@Override
public void onSuccess(PollingStation.ScannedData result) {
if (result.getDataCase().equals(PollingStation.ScannedData.DataCase.BALLOT)) {
// TODO: Set request ID correctly?
int requestID = 1;
long serialNumber = result.getBallot().getSignedEncryptedBallot().getEncryptedBallot().getSerialNumber();
queue.add(new ReceivedScanCommand(requestID, serialNumber));
} else if (result.getDataCase().equals(PollingStation.ScannedData.DataCase.ERROR)) {
// TODO: Deal with scan errors
}
}
@Override
public void onFailure(Throwable t) {
// TODO: deal with this? When is this called?
}
});
} }
public void run() { public void run() {

View File

@ -6,6 +6,7 @@ package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.BoolValue; import com.google.protobuf.BoolValue;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation; import meerkat.protobuf.PollingStation;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -17,6 +18,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import java.io.IOException; import java.io.IOException;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_CONNECT_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH; import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH; import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF; import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF;
@ -49,6 +51,15 @@ public class PollingStationScannerWebApp implements PollingStationScanner.Produc
} }
@POST
@Path(POLLING_STATION_WEB_SCANNER_CONNECT_PATH)
@Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF)
@Override
public BoolValue connect(PollingStation.ConnectionClientData clientData) {
return null;
}
@POST @POST
@Path(POLLING_STATION_WEB_SCANNER_SCAN_PATH) @Path(POLLING_STATION_WEB_SCANNER_SCAN_PATH)
@Consumes(MEDIATYPE_PROTOBUF) @Consumes(MEDIATYPE_PROTOBUF)
@ -58,6 +69,7 @@ public class PollingStationScannerWebApp implements PollingStationScanner.Produc
boolean handled = false; boolean handled = false;
// TODO:
for (FutureCallback<PollingStation.ScannedData> callback : callbacks){ for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
callback.onSuccess(scannedData); callback.onSuccess(scannedData);
@ -71,26 +83,4 @@ public class PollingStationScannerWebApp implements PollingStationScanner.Produc
} }
@POST
@Path(POLLING_STATION_WEB_SCANNER_ERROR_PATH)
@Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF)
@Override
public BoolValue reportScanError(PollingStation.ErrorMsg errorMsg) {
boolean handled = false;
for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
callback.onFailure(new IOException(errorMsg.getMsg()));
handled = true;
}
return BoolValue.newBuilder()
.setValue(handled)
.build();
}
} }

View File

@ -1,16 +1,20 @@
package meerkat.pollingstation; package meerkat.pollingstation;
import meerkat.protobuf.PollingStation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* Created by Laura on 3/20/2017. * Created by Laura on 3/20/2017.
*/ */
public class PollingStationToyRun { public class PollingStationToyRun {
final Logger logger = LoggerFactory.getLogger(getClass());
private static PollingStationScanner.Consumer scanner; private static PollingStationScanner.Consumer scanner;
private static final String ADDRESS = "http://localhost"; private static final String CONTEXT_PATH = "/scan";
private static final String SUB_ADDRESS = "";
private static final int PORT = 8080; private static final int PORT = 8080;
public static void main(String [] args) { public static void main(String [] args) throws Exception {
System.err.println("Setting up Scanner WebApp!"); System.err.println("Setting up Scanner WebApp!");
// scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS); // scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
@ -22,14 +26,17 @@ public class PollingStationToyRun {
// System.err.println("Could not start server: " + e.getMessage()); // System.err.println("Could not start server: " + e.getMessage());
// } // }
ReceiverScanHandler serverController = new ReceiverScanHandler(PORT, SUB_ADDRESS); scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
PollingStation.ConnectionServerData serverData = scanner.start(false);
PollingStationMainController controller = new PollingStationMainController(); PollingStationMainController controller = new PollingStationMainController();
controller.init(serverController); controller.init(scanner);
Thread controllerThread = new Thread(controller); Thread controllerThread = new Thread(controller);
controllerThread.setName("Meerkat PS-Controller Thread"); controllerThread.setName("Meerkat PS-Controller Thread");
Thread serverThread = new Thread(serverController); // Thread serverThread = new Thread(serverController);
serverThread.setName("Meerkat PS-ServerScanner Thread"); // serverThread.setName("Meerkat PS-ServerScanner Thread");
controllerThread.start(); controllerThread.start();
serverThread.start(); serverThread.start();

View File

@ -1,9 +1,11 @@
package meerkat.pollingstation; package meerkat.pollingstation;
import java.security.SecureRandom;
import java.util.List; import java.util.List;
import java.util.LinkedList; import java.util.LinkedList;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import meerkat.protobuf.PollingStation;
import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.*; import org.eclipse.jetty.servlet.*;
@ -17,20 +19,22 @@ import meerkat.rest.*;
* Created by Arbel on 05/05/2016. * Created by Arbel on 05/05/2016.
*/ */
public class PollingStationWebScanner implements PollingStationScanner.Consumer{ public class PollingStationWebScanner implements PollingStationScanner.Consumer {
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller"; public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
private final Server server; private final Server server;
private final List<FutureCallback<ScannedData>> callbacks; private final List<FutureCallback<ScannedData>> callbacks;
boolean verifyScanner;
byte nonce[];
public PollingStationWebScanner(int port, String subAddress) { public PollingStationWebScanner(int port, String contextPath) {
callbacks = new LinkedList<>(); callbacks = new LinkedList<>();
server = new Server(port); server = new Server(port);
ServletContextHandler servletContextHandler = new ServletContextHandler(server, subAddress); ServletContextHandler servletContextHandler = new ServletContextHandler(server, contextPath);
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, (Iterable<FutureCallback<ScannedData>>) callbacks); servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, (Iterable<FutureCallback<ScannedData>>) callbacks);
ResourceConfig resourceConfig = new ResourceConfig(PollingStationScannerWebApp.class); ResourceConfig resourceConfig = new ResourceConfig(PollingStationScannerWebApp.class);
@ -42,9 +46,17 @@ public class PollingStationWebScanner implements PollingStationScanner.Consumer{
servletContextHandler.addServlet(servletHolder, "/*"); servletContextHandler.addServlet(servletHolder, "/*");
} }
@Override @Override
public void start() throws Exception { public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
this.verifyScanner = verifyScanner;
nonce = new byte[32];
new SecureRandom().nextBytes(nonce);
server.start(); server.start();
return null;
} }
@Override @Override

View File

@ -24,7 +24,7 @@ import java.util.concurrent.LinkedBlockingQueue;
/** /**
* Created by Laura on 3/20/2017. * Created by Laura on 3/20/2017.
*/ */
public class ReceiverScanHandler implements PollingStationScanner.Consumer, Runnable{ public class ReceiverScanHandler implements PollingStationScanner.Consumer, Runnable {
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller"; public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
@ -78,7 +78,7 @@ public class ReceiverScanHandler implements PollingStationScanner.Consumer, Runn
} }
@Override @Override
public void start() throws Exception { public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
server.start(); server.start();
while (! wasShutDownCalled()) { while (! wasShutDownCalled()) {
try { try {

View File

@ -5,6 +5,7 @@ import com.google.protobuf.BoolValue;
import meerkat.pollingstation.controller.callbacks.ScanCallback; import meerkat.pollingstation.controller.callbacks.ScanCallback;
import meerkat.pollingstation.controller.callbacks.ScanDataCallback; import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
import meerkat.pollingstation.controller.callbacks.ScanErrorCallback; import meerkat.pollingstation.controller.callbacks.ScanErrorCallback;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation; import meerkat.protobuf.PollingStation;
import javax.annotation.PostConstruct; import javax.annotation.PostConstruct;
@ -16,6 +17,7 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_CONNECT_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH; import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH; import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF; import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF;
@ -48,12 +50,37 @@ public class ReceiverWebAPI implements PollingStationScanner.Producer {
} }
@POST
@Path(POLLING_STATION_WEB_SCANNER_CONNECT_PATH)
@Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF)
@Override
public BoolValue connect(PollingStation.ConnectionClientData clientData) {
boolean sent = false;
try {
ScanDataCallback callback = new ScanDataCallback(scannedData);
callbacks.add(callback);
sent = true;
} catch (Exception e) {
e.printStackTrace();
}
return BoolValue.newBuilder()
.setValue(sent)
.build();
}
@POST @POST
@Path(POLLING_STATION_WEB_SCANNER_SCAN_PATH) @Path(POLLING_STATION_WEB_SCANNER_SCAN_PATH)
@Consumes(MEDIATYPE_PROTOBUF) @Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF) @Produces(MEDIATYPE_PROTOBUF)
@Override @Override
public BoolValue newScan(PollingStation.ScannedData scannedData) { public BoolValue newScan(PollingStation.ScannedData scannedData, Crypto.Signature scannedDataSignature) {
boolean sent = false; boolean sent = false;
@ -83,7 +110,7 @@ public class ReceiverWebAPI implements PollingStationScanner.Producer {
@Consumes(MEDIATYPE_PROTOBUF) @Consumes(MEDIATYPE_PROTOBUF)
@Produces(MEDIATYPE_PROTOBUF) @Produces(MEDIATYPE_PROTOBUF)
@Override @Override
public BoolValue reportScanError(PollingStation.ErrorMsg errorMsg) { public BoolValue reportScanError(PollingStation.ErrorMsg errorMsg, Crypto.Signature errorMsgSignature) {
boolean handled = false; boolean handled = false;
@ -104,4 +131,5 @@ public class ReceiverWebAPI implements PollingStationScanner.Producer {
} }
} }

View File

@ -1,6 +1,6 @@
package meerkat.pollingstation.controller; package meerkat.pollingstation.controller;
import meerkat.pollingstation.ReceiverScanHandler; import meerkat.pollingstation.PollingStationScanner;
/** /**
* An interface for the controller component of the polling station * An interface for the controller component of the polling station
@ -11,12 +11,10 @@ public interface PollingStationControllerInterface extends Runnable {
* (see VotingBoothController) * (see VotingBoothController)
*/ */
// TODO: 4/16/2017 complete with proper arguments and exceptions // TODO: 4/16/2017 complete with proper arguments and exceptions
public void init(ReceiverScanHandler server); public void init(PollingStationScanner.Consumer server);
/** /**
* an asynchronous call from Admin Console (If there is such one implemented) to shut down the system * an asynchronous call from Admin Console (If there is such one implemented) to shut down the system
*/ */
public void callShutDown(); public void callShutDown();
} }

View File

@ -3,6 +3,6 @@ package meerkat.pollingstation.controller.callbacks;
/** /**
* Created by Laura on 3/20/2017. * Created by Laura on 3/20/2017.
*/ */
public abstract class ScanCallback { public interface ScanCallback {
} }

View File

@ -6,7 +6,7 @@ import meerkat.protobuf.PollingStation;
/** /**
* Created by Laura on 3/20/2017. * Created by Laura on 3/20/2017.
*/ */
public class ScanDataCallback extends ScanCallback implements FutureCallback<PollingStation.ScannedData> { public class ScanDataCallback implements ScanCallback, FutureCallback<PollingStation.ScannedData> {
private final PollingStation.ScannedData expectedData; private final PollingStation.ScannedData expectedData;
private boolean dataIsAsExpected; private boolean dataIsAsExpected;
@ -22,7 +22,8 @@ public class ScanDataCallback extends ScanCallback implements FutureCallback<Pol
@Override @Override
public void onSuccess(PollingStation.ScannedData result) { public void onSuccess(PollingStation.ScannedData result) {
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
dataIsAsExpected = result.getBallot().getChannel().equals(expectedData.getBallot().getChannel());
} }
@Override @Override

View File

@ -2,6 +2,7 @@ package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import meerkat.protobuf.PollingStation;
import meerkat.protobuf.PollingStation.ErrorMsg; import meerkat.protobuf.PollingStation.ErrorMsg;
import meerkat.protobuf.PollingStation.ScannedData; import meerkat.protobuf.PollingStation.ScannedData;
import meerkat.rest.Constants; import meerkat.rest.Constants;
@ -10,6 +11,8 @@ import meerkat.rest.ProtobufMessageBodyWriter;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.ws.rs.client.Client; import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.ClientBuilder;
@ -24,18 +27,16 @@ import static org.hamcrest.MatcherAssert.assertThat;
public class Receiver_ClientTest { public class Receiver_ClientTest {
final Logger logger = LoggerFactory.getLogger(getClass());
private PollingStationScanner.Consumer scanner; private PollingStationScanner.Consumer scanner;
private static final int PSC_PORT = 8880; private static final String CONTEXT_PATH = "/scan";
private static final String PSC_HOST = "localhost";
private static final String PSC_SCANNER_PATH = "";
private static final String PSC_URL = "http://" + PSC_HOST + ":" + PSC_PORT + PSC_SCANNER_PATH;
private Semaphore semaphore; private Semaphore semaphore;
private Throwable thrown; private Throwable thrown;
private boolean dataIsAsExpected; private boolean dataIsAsExpected;
PollingStation.ConnectionServerData serverData = null;
private class ScanHandler implements FutureCallback<ScannedData> { private class ScanHandler implements FutureCallback<ScannedData> {
private final ScannedData expectedData; private final ScannedData expectedData;
@ -58,43 +59,26 @@ public class Receiver_ClientTest {
} }
} }
private class ErrorHandler implements FutureCallback<ScannedData> {
private final String expectedErrorMessage;
public ErrorHandler(String expectedErrorMessage) {
this.expectedErrorMessage = expectedErrorMessage;
}
@Override
public void onSuccess(ScannedData result) {
dataIsAsExpected = false;
semaphore.release();
}
@Override
public void onFailure(Throwable t) {
dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
semaphore.release();
}
}
@Before @Before
public void init() { public void init() {
System.err.println("Setting up Scanner WebApp!"); System.err.println("Setting up Scanner WebApp!");
scanner = new ReceiverScanHandler(PSC_PORT, PSC_SCANNER_PATH); scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
semaphore = new Semaphore(0); semaphore = new Semaphore(0);
thrown = null; thrown = null;
PollingStation.ConnectionServerData serverData = null;
try { try {
scanner.start(); serverData = scanner.start(false);
} catch (Exception e) { } catch (Exception e) {
assertThat("Could not start server: " + e.getMessage(), false); assertThat("Could not start server: " + e.getMessage(), false);
} }
logger.info("Started scanner web service. API endpoint: {}", serverData.getServerUrl());
} }
@Test @Test
@ -103,13 +87,17 @@ public class Receiver_ClientTest {
byte[] data = {(byte) 1, (byte) 2}; byte[] data = {(byte) 1, (byte) 2};
ScannedData scannedData = ScannedData.newBuilder() ScannedData scannedData = ScannedData.newBuilder()
.setChannel(ByteString.copyFrom(data)) .setBallot(PollingStation.ScannedBallot.newBuilder()
.setChannel(ByteString.copyFrom(data))
)
.build(); .build();
scanner.subscribe(new ScanHandler(scannedData)); scanner.subscribe(new ScanHandler(scannedData));
ScannerClientAPI scannerClient = new ScannerClientAPI(PSC_URL + POLLING_STATION_WEB_SCANNER_SCAN_PATH); PollingStationScanner.Producer scannerClient = new ScannerClientAPI()
scannerClient.sendScan(scannedData);
ScannerClientAPI(PSC_URL + POLLING_STATION_WEB_SCANNER_SCAN_PATH);
scannerClient.newScan(scannedData, null);
semaphore.acquire(); semaphore.acquire();
assertThat("Scanner has thrown an error", thrown == null); assertThat("Scanner has thrown an error", thrown == null);
@ -126,7 +114,10 @@ public class Receiver_ClientTest {
scanner.subscribe(new ErrorHandler(errorMsg.getMsg())); scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
ScannerClientAPI scannerClient = new ScannerClientAPI(PSC_URL + POLLING_STATION_WEB_SCANNER_ERROR_PATH); ScannerClientAPI scannerClient = new ScannerClientAPI();
PSC_URL + POLLING_STATION_WEB_SCANNER_ERROR_PATH);
scannerClient.sendError(errorMsg); scannerClient.sendError(errorMsg);
semaphore.acquire(); semaphore.acquire();

View File

@ -106,7 +106,6 @@ idea {
def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java" def srcDir = "${protobuf.generatedFilesBaseDir}/$sourceSet.name/java"
println "Adding $srcDir"
// add protobuf generated sources to generated source dir. // add protobuf generated sources to generated source dir.
if ("test".equals(sourceSet.name)) { if ("test".equals(sourceSet.name)) {
testSourceDirs += file(srcDir) testSourceDirs += file(srcDir)

View File

@ -9,5 +9,6 @@ public interface PollingStationConstants {
public static final String POLLING_STATION_WEB_SCANNER_SCAN_PATH = "/scan"; public static final String POLLING_STATION_WEB_SCANNER_SCAN_PATH = "/scan";
public static final String POLLING_STATION_WEB_SCANNER_ERROR_PATH = "/error"; public static final String POLLING_STATION_WEB_SCANNER_ERROR_PATH = "/error";
public static final String POLLING_STATION_WEB_SCANNER_CONNECT_PATH = "/connect";
} }

View File

@ -2,6 +2,7 @@ package meerkat.pollingstation;
import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.FutureCallback;
import com.google.protobuf.BoolValue; import com.google.protobuf.BoolValue;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation.*; import meerkat.protobuf.PollingStation.*;
/** /**
* Created by Arbel on 05/05/2016. * Created by Arbel on 05/05/2016.
@ -17,10 +18,13 @@ public interface PollingStationScanner {
public interface Consumer { public interface Consumer {
/** /**
* Sets up the connection to the scanner and begins receiving scans * Sets up the connection to the scanner and begins receiving scans.
* Returns the data the scanner client will need to connect
* If verifyScanner is true, will verify that scanner has the correct nonce before
* allowing connection, and verify signatures on scanned data.
* @throws Exception when the operation fails * @throws Exception when the operation fails
*/ */
public void start() throws Exception; public ConnectionServerData start(boolean verifyScanner) throws Exception;
/** /**
* Closes the connection to the scanner * Closes the connection to the scanner
@ -34,7 +38,6 @@ public interface PollingStationScanner {
* @param scanCallback is the handler for scanned data * @param scanCallback is the handler for scanned data
*/ */
public void subscribe(FutureCallback<ScannedData> scanCallback); public void subscribe(FutureCallback<ScannedData> scanCallback);
} }
/** /**
@ -42,6 +45,13 @@ public interface PollingStationScanner {
*/ */
public interface Producer { public interface Producer {
/**
* Connect to the polling station server.
* The client data contains the scanner ID and optional verification data.
* @return
*/
public BoolValue connect(ConnectionClientData clientData);
/** /**
* Sends a scan to all subscribers * Sends a scan to all subscribers
* @param scannedData contains the scanned data * @param scannedData contains the scanned data
@ -49,13 +59,6 @@ public interface PollingStationScanner {
*/ */
public BoolValue newScan(ScannedData scannedData); public BoolValue newScan(ScannedData scannedData);
/**
* Notifies subscribers about an error that occurred during scan
* @param errorMsg is the error that occurred
* @return a BoolValue containing TRUE iff the error has been sent to at least one subscriber
*/
public BoolValue reportScanError(ErrorMsg errorMsg);
} }
} }

View File

@ -1,7 +1,7 @@
package meerkat.pollingstation; package meerkat.pollingstation;
import com.google.protobuf.BoolValue; import com.google.protobuf.BoolValue;
import meerkat.protobuf.BulletinBoardAPI; import meerkat.protobuf.Crypto;
import meerkat.protobuf.PollingStation; import meerkat.protobuf.PollingStation;
import meerkat.rest.Constants; import meerkat.rest.Constants;
import meerkat.rest.ProtobufMessageBodyReader; import meerkat.rest.ProtobufMessageBodyReader;
@ -13,13 +13,10 @@ import javax.ws.rs.client.Entity;
import javax.ws.rs.client.WebTarget; import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
/** /**
* Created by Laura on 3/20/2017. * Created by Laura on 3/20/2017.
*/ */
public class ScannerClientAPI { public class ScannerClientAPI implements PollingStationScanner.Producer {
private Client client; private Client client;
private WebTarget webTarget; private WebTarget webTarget;
@ -30,27 +27,38 @@ public class ScannerClientAPI {
webTarget = client.target(url); webTarget = client.target(url);
} }
/** /**
* Sends the scanned data to the polling station server * Sends the scanned data to the polling station server
* @param scannedData * @param scannedData
* @return boolean value: true if the data was sent successfully, false otherwise * @return boolean value: true if the data was sent successfully, false otherwise
*/ */
public boolean sendScan(PollingStation.ScannedData scannedData) { @Override
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF)); public BoolValue newScan(PollingStation.ScannedData scannedData) {
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF),
);
BoolValue res = response.readEntity(BoolValue.class); BoolValue res = response.readEntity(BoolValue.class);
response.close(); response.close();
return res.getValue(); return res.getValue();
} }
/**
* Sends the error message to the polling station server public void close() {
* @param errorMsg client.close();
* @return boolean value: true if the error message was sent successfully, false otherwise
*/ }
public boolean sendError(PollingStation.ErrorMsg errorMsg) {
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF)); @Override
BoolValue res = response.readEntity(BoolValue.class); public BoolValue connect(PollingStation.ConnectionClientData clientData) {
response.close(); return null;
return res.getValue(); }
return null;
}
@Override
public BoolValue reportScanError(PollingStation.ErrorMsg errorMsg, Crypto.Signature errorMsgSignature) {
return null;
} }
} }

View File

@ -0,0 +1,54 @@
syntax = "proto3";
package meerkat;
option java_package = "meerkat.protobuf";
import "meerkat/crypto.proto";
import "meerkat/voting.proto";
// Data sent (out-of-band) by the server that the client will require to set up a connection
message ConnectionServerData {
// Web-API URL for the server
string server_url = 1;
// random nonce used for verification of the client.
bytes nonce = 2;
}
// Data sent by the client in order to set up a connection
message ConnectionClientData {
// Key used to identify the client
SignatureVerificationKey scannerPK = 1;
// Nonce sent by connection-server
bytes nonce = 2;
}
message ScannedBallot {
bytes channel = 1;
SignedEncryptedBallot signed_encrypted_ballot = 2;
}
// Container for error messages
message ScanError {
string msg = 1;
}
// Container for scanned data
message ScannedData {
oneof data {
ScannedBallot ballot = 2;
ScanError error = 3;
}
uint64 serial = 4; // Serial number of the message
bytes scannerId = 5; // hash of the scannerPK
Signature scannerSig = 6; // signature on the serialzed form of data.
}