Mostly finished with scanAPI refactoring
parent
cb1d2343c4
commit
6f35f105c5
|
@ -53,6 +53,11 @@ public class GenericBulletinBoardSignature implements BulletinBoardSignature {
|
|||
signer.loadVerificationCertificate(cert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString computeCertificateFingerprint(Crypto.SignatureVerificationKey cert) throws CertificateException {
|
||||
return signer.computeCertificateFingerprint(cert);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
|
||||
signer.loadVerificationCertificates(certStream);
|
||||
|
|
|
@ -34,6 +34,13 @@ public interface DigitalSignature {
|
|||
throws CertificateException;
|
||||
|
||||
|
||||
/**
|
||||
* Compute a fingerprint (SHA256 hash) of a certificate.
|
||||
* @param cert
|
||||
* @return
|
||||
*/
|
||||
public ByteString computeCertificateFingerprint(SignatureVerificationKey cert) throws CertificateException;
|
||||
|
||||
/**
|
||||
* Load a set of certificates from an input stream.
|
||||
* This will consume the entire stream.
|
||||
|
|
|
@ -20,6 +20,7 @@ import java.io.InputStream;
|
|||
import java.math.BigInteger;
|
||||
import java.security.*;
|
||||
import java.security.cert.*;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.interfaces.ECPrivateKey;
|
||||
|
||||
|
||||
|
@ -68,26 +69,21 @@ public class ECDSADeterministicSignature extends ECDSASignature {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
|
||||
throws CertificateException, UnrecoverableKeyException, IOException {
|
||||
super.loadSigningCertificate(keyStoreBuilder);
|
||||
public void loadSigningCertificate(Certificate cert, PrivateKey key) throws InvalidKeyException, CertificateException {
|
||||
super.loadSigningCertificate(cert, key);
|
||||
|
||||
if (!(loadedSigningKey instanceof ECPrivateKey)) {
|
||||
logger.error("Wrong private key type (expected ECPrivateKey, got {})", loadedSigningKey.getClass());
|
||||
throw new CertificateException("Wrong signing key type!");
|
||||
}
|
||||
ECPrivateKey key = (ECPrivateKey) loadedSigningKey;
|
||||
ECPrivateKey eckey = (ECPrivateKey) loadedSigningKey;
|
||||
|
||||
AsymmetricKeyParameter baseParams;
|
||||
try {
|
||||
baseParams = ECUtil.generatePrivateKeyParameter(key);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new UnrecoverableKeyException("Couldn't convert private key");
|
||||
}
|
||||
baseParams = ECUtil.generatePrivateKeyParameter(eckey);
|
||||
|
||||
if (!(baseParams instanceof ECPrivateKeyParameters)) {
|
||||
logger.error("Error converting to bouncycastle type! (got {})", baseParams.getClass());
|
||||
throw new UnrecoverableKeyException("Wrong signing key type!");
|
||||
throw new InvalidKeyException("Wrong signing key type!");
|
||||
}
|
||||
|
||||
ECPrivateKeyParameters params = (ECPrivateKeyParameters) baseParams;
|
||||
|
|
|
@ -135,11 +135,33 @@ public class ECDSASignature implements DigitalSignatureGenerator {
|
|||
loadVerificationCertificates(certStream);
|
||||
}
|
||||
|
||||
|
||||
public Collection<? extends Certificate> deserializeCertificates(InputStream certStream)
|
||||
throws CertificateException {
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509);
|
||||
|
||||
Collection<? extends Certificate> certs = certificateFactory.generateCertificates(certStream);
|
||||
return certs;
|
||||
}
|
||||
|
||||
public Collection<? extends Certificate> deserializeCertificates(Crypto.SignatureVerificationKey cert)
|
||||
throws CertificateException {
|
||||
ByteArrayInputStream certStream = new ByteArrayInputStream(cert.getData().toByteArray());
|
||||
return deserializeCertificates(certStream);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteString computeCertificateFingerprint(Crypto.SignatureVerificationKey cert)
|
||||
throws CertificateException {
|
||||
Collection<? extends Certificate> certs = deserializeCertificates(cert);
|
||||
return computeCertificateFingerprint(certs.iterator().next());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void loadVerificationCertificates(InputStream certStream)
|
||||
throws CertificateException {
|
||||
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509);
|
||||
Collection<? extends Certificate> certs = certificateFactory.generateCertificates(certStream);
|
||||
Collection<? extends Certificate> certs = deserializeCertificates(certStream);
|
||||
for (Certificate cert : certs) {
|
||||
loadVerificationCertificate(cert);
|
||||
}
|
||||
|
@ -321,6 +343,8 @@ public class ECDSASignature implements DigitalSignatureGenerator {
|
|||
}
|
||||
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
|
||||
if (key instanceof PrivateKey) {
|
||||
|
||||
// Here is where we *actually* load the private key
|
||||
loadSigningCertificate(cert, (PrivateKey) key);
|
||||
return;
|
||||
} else {
|
||||
|
|
|
@ -11,16 +11,13 @@ import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB
|
|||
* Created by Laura on 3/20/2017.
|
||||
*/
|
||||
public class PollingStationClientToyRun {
|
||||
|
||||
private static final String PSC_URL = "http://192.168.70.10:8880/";
|
||||
|
||||
public static void main(String [] args) {
|
||||
int channel = 10;
|
||||
System.out.println("Message to send: "+channel);
|
||||
byte[] data = ByteBuffer.allocate(4).putInt(channel).array();
|
||||
// byte[] data = {(byte) 1, (byte) 2};
|
||||
|
||||
PollingStation.ScannedData scannedData = PollingStation.ScannedData.newBuilder()
|
||||
PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder()
|
||||
.setChannel(ByteString.copyFrom(data))
|
||||
.build();
|
||||
|
||||
|
@ -29,9 +26,10 @@ public class PollingStationClientToyRun {
|
|||
// System.out.println("Channel int initial:"+channel);
|
||||
// System.out.println("Channel int retrieved:"+retrieved);
|
||||
|
||||
ScannerClientAPI scannerClient = new ScannerClientAPI(PSC_URL);
|
||||
boolean sent = scannerClient.sendScan(scannedData);
|
||||
PollingStationScanner.ScannerClient scannerClient = new ScannerClientAPI();
|
||||
|
||||
System.out.println("Message sent successfully: "+sent);
|
||||
// boolean sent = scannerClient.sendScan(scannedData);
|
||||
//
|
||||
// System.out.println("Message sent successfully: "+sent);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,7 @@ package meerkat.pollingstation;
|
|||
* Created by Arbel on 5/31/2016.
|
||||
*/
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.BoolValue;
|
||||
import meerkat.crypto.DigitalSignature;
|
||||
import meerkat.crypto.concrete.ECDSASignature;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
@ -43,7 +40,7 @@ public class PollingStationScannerWebApp implements PollingStationScanner.Pollin
|
|||
@PostConstruct
|
||||
@SuppressWarnings("unchecked")
|
||||
public void init() throws Exception {
|
||||
Object context = servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME);
|
||||
Object context = servletContext.getAttribute(PollingStationWebScanner.SCAN_HANDLER_ATTRIBUTE_NAME);
|
||||
|
||||
try {
|
||||
requestHandler = (PollingStationWebScanner.ScanRequestHandler) context;
|
||||
|
|
|
@ -8,14 +8,14 @@ import org.slf4j.LoggerFactory;
|
|||
* Created by Laura on 3/20/2017.
|
||||
*/
|
||||
public class PollingStationToyRun {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
final static Logger logger = LoggerFactory.getLogger(PollingStation.class);
|
||||
|
||||
private static PollingStationScanner.PollingStationServer scanner;
|
||||
private static final String CONTEXT_PATH = "/scan";
|
||||
private static final int PORT = 8080;
|
||||
private static final int PORT = 0;
|
||||
|
||||
public static void main(String [] args) throws Exception {
|
||||
System.err.println("Setting up Scanner WebApp!");
|
||||
logger.debug("Setting up Scanner WebApp!");
|
||||
|
||||
// scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
|
||||
//
|
||||
|
@ -27,19 +27,12 @@ public class PollingStationToyRun {
|
|||
// }
|
||||
|
||||
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
|
||||
PollingStation.ConnectionServerData serverData = scanner.start(false);
|
||||
|
||||
PollingStation.ConnectionServerData serverData = scanner.start(true);
|
||||
|
||||
PollingStationMainController controller = new PollingStationMainController();
|
||||
controller.init(scanner);
|
||||
|
||||
Thread controllerThread = new Thread(controller);
|
||||
controllerThread.setName("Meerkat PS-Controller Thread");
|
||||
// Thread serverThread = new Thread(serverController);
|
||||
// serverThread.setName("Meerkat PS-ServerScanner Thread");
|
||||
|
||||
controllerThread.start();
|
||||
serverThread.start();
|
||||
|
||||
Thread.currentThread().setName("Meerkat PS-Controller Thread");
|
||||
controller.run();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import com.google.common.util.concurrent.FutureCallback;
|
|||
import com.google.protobuf.ByteString;
|
||||
import meerkat.crypto.DigitalSignature;
|
||||
import meerkat.crypto.concrete.ECDSASignature;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.*;
|
||||
|
@ -28,7 +29,7 @@ import org.slf4j.LoggerFactory;
|
|||
*/
|
||||
|
||||
public class PollingStationWebScanner implements PollingStationScanner.PollingStationServer {
|
||||
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
|
||||
public final static String SCAN_HANDLER_ATTRIBUTE_NAME = "controller";
|
||||
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
|
@ -59,6 +60,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
|||
*
|
||||
*/
|
||||
DigitalSignature verifier;
|
||||
ByteString connectedScannerID;
|
||||
|
||||
private final Server server;
|
||||
|
||||
|
@ -75,17 +77,35 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
|||
return false;
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (verifySignatures) {
|
||||
|
||||
if (verifySignatures) {
|
||||
try {
|
||||
if (data.getIdCase() != PollingStation.ConnectionClientData.IdCase.SCANNERPK)
|
||||
throw new CertificateException("Connection request doesn't include certificate!");
|
||||
|
||||
verifier.loadVerificationCertificate(data.getScannerPK());
|
||||
} catch (CertificateException e) {
|
||||
logger.warn("Scanner connection with bad certificate: {}", e);
|
||||
return false;
|
||||
Crypto.SignatureVerificationKey pk = data.getScannerPK();
|
||||
connectedScannerID = verifier.computeCertificateFingerprint(pk);
|
||||
verifier.loadVerificationCertificate(pk);
|
||||
|
||||
} else {
|
||||
// If we don't verify certs, we can still get a public key, in which case
|
||||
// the ID will be the hash of the public key.
|
||||
switch (data.getIdCase()) {
|
||||
case SCANNERID:
|
||||
connectedScannerID = data.getScannerId();
|
||||
break;
|
||||
case SCANNERPK:
|
||||
connectedScannerID = verifier.computeCertificateFingerprint(data.getScannerPK());
|
||||
break;
|
||||
default:
|
||||
logger.warn("Scanner connection with no ID or certificate!");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
} catch (CertificateException e) {
|
||||
logger.warn("Scanner connection with bad certificate: {}", e);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -95,7 +115,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
|||
ScannedData scannedData = data.getData();
|
||||
if (verifySignatures) {
|
||||
ByteString scannerID = scannedData.getScannerId();
|
||||
if (!scannerID.equals(verifier.getSignerID())) {
|
||||
if (!scannerID.equals(connectedScannerID)) {
|
||||
logger.warn("Scanner ID doesn't match connection public key");
|
||||
return false;
|
||||
}
|
||||
|
@ -141,7 +161,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
|||
server = new Server(port);
|
||||
|
||||
ServletContextHandler servletContextHandler = new ServletContextHandler(server, contextPath);
|
||||
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, callbacks);
|
||||
servletContextHandler.setAttribute(SCAN_HANDLER_ATTRIBUTE_NAME, scanRequestHandler);
|
||||
|
||||
ResourceConfig resourceConfig = new ResourceConfig(PollingStationScannerWebApp.class);
|
||||
resourceConfig.register(ProtobufMessageBodyReader.class);
|
||||
|
@ -153,14 +173,16 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
|||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
|
||||
this.verifyNonce = this.verifySignatures = verifyScanner;
|
||||
nonce = new SecureRandom().nextLong();
|
||||
|
||||
server.start();
|
||||
return null;
|
||||
return PollingStation.ConnectionServerData.newBuilder()
|
||||
.setNonce(nonce)
|
||||
.setServerUrl(server.getURI().toString())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,130 +0,0 @@
|
|||
package meerkat.pollingstation;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.pollingstation.controller.callbacks.ScanCallback;
|
||||
import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
|
||||
import meerkat.pollingstation.controller.callbacks.ScanErrorCallback;
|
||||
import meerkat.pollingstation.controller.commands.PollingStationCommand;
|
||||
import meerkat.pollingstation.controller.commands.ReceivedScanCommand;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.glassfish.jersey.server.ResourceConfig;
|
||||
import org.glassfish.jersey.servlet.ServletContainer;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
/**
|
||||
* Created by Laura on 3/20/2017.
|
||||
*/
|
||||
public class ReceiverScanHandler implements PollingStationScanner.PollingStationServer, Runnable {
|
||||
|
||||
public final static String CALLBACKS_ATTRIBUTE_NAME = "controller";
|
||||
|
||||
private final Server server;
|
||||
private final List<FutureCallback<PollingStation.ScannedData>> callbacks;
|
||||
|
||||
private LinkedBlockingQueue<ScanCallback> queue;
|
||||
private LinkedBlockingQueue<PollingStationCommand> controllerQueue;
|
||||
private volatile boolean shutDownHasBeenCalled;
|
||||
|
||||
public ReceiverScanHandler(int port, String subAddress) {
|
||||
|
||||
callbacks = new LinkedList<>();
|
||||
|
||||
server = new Server(port);
|
||||
|
||||
queue = new LinkedBlockingQueue<>();
|
||||
shutDownHasBeenCalled = false;
|
||||
|
||||
ServletContextHandler servletContextHandler = new ServletContextHandler(server, subAddress);
|
||||
servletContextHandler.setAttribute(CALLBACKS_ATTRIBUTE_NAME, (LinkedBlockingQueue<ScanCallback>) queue);
|
||||
|
||||
|
||||
ResourceConfig resourceConfig = new ResourceConfig(ReceiverWebAPI.class);
|
||||
resourceConfig.register(ProtobufMessageBodyReader.class);
|
||||
resourceConfig.register(ProtobufMessageBodyWriter.class);
|
||||
|
||||
ServletHolder servletHolder = new ServletHolder(new ServletContainer(resourceConfig));
|
||||
|
||||
servletContextHandler.addServlet(servletHolder, "/*");
|
||||
|
||||
|
||||
}
|
||||
|
||||
public void setControllerQueue(LinkedBlockingQueue<PollingStationCommand> q) {
|
||||
this.controllerQueue = q;
|
||||
}
|
||||
|
||||
private boolean wasShutDownCalled () {
|
||||
return shutDownHasBeenCalled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
start();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.err.println("Could not start server: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
|
||||
server.start();
|
||||
while (! wasShutDownCalled()) {
|
||||
try {
|
||||
handleSingleCallback(queue.take());
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
System.err.println("Interrupted while reading from command queue " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() throws Exception {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void subscribe(FutureCallback<PollingStation.ScannedData> scanCallback) {
|
||||
callbacks.add(scanCallback);
|
||||
}
|
||||
|
||||
|
||||
private void handleSingleCallback(ScanCallback callback) {
|
||||
if (callback instanceof ScanDataCallback) {
|
||||
doScannedData((ScanDataCallback) callback);
|
||||
}
|
||||
else if (callback instanceof ScanErrorCallback) {
|
||||
doScanError((ScanErrorCallback) callback);
|
||||
}
|
||||
else {
|
||||
System.out.println("in the else");
|
||||
// logger.error("handleSingleCommand: unknown type of PollingStationCommand received: " + command.getClass().getName());
|
||||
// doReportErrorAndForceRestart(systemMessages.get(StorageManager.SOMETHING_WRONG_MESSAGE));
|
||||
// doReportErrorAndForceRestart("error message to define");
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: 4/27/2017 set requestID properly
|
||||
private void doScannedData(ScanDataCallback cb) {
|
||||
PollingStation.ScannedData scan = cb.getScannedData();
|
||||
// String channel = scan.getChannel().toStringUtf8();
|
||||
int requestID = 1;
|
||||
long serialNumber = scan.getSignedEncryptedBallot().getEncryptedBallot().getSerialNumber();
|
||||
controllerQueue.add(new ReceivedScanCommand(requestID, serialNumber));
|
||||
}
|
||||
|
||||
private void doScanError(ScanErrorCallback cb) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,134 +0,0 @@
|
|||
package meerkat.pollingstation;
|
||||
|
||||
import com.google.protobuf.BoolValue;
|
||||
import meerkat.pollingstation.controller.callbacks.ScanCallback;
|
||||
import meerkat.pollingstation.controller.callbacks.ScanDataCallback;
|
||||
import meerkat.pollingstation.controller.callbacks.ScanErrorCallback;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.Context;
|
||||
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_SCAN_PATH;
|
||||
import static meerkat.rest.Constants.MEDIATYPE_PROTOBUF;
|
||||
|
||||
/**
|
||||
* Implements a Web-App interface for {@link PollingStationScanner.ScannerClient}
|
||||
* This class depends on {@link meerkat.pollingstation.ReceiverScanHandler} and works in conjunction with it
|
||||
*/
|
||||
@Path("/")
|
||||
public class ReceiverWebAPI implements PollingStationScanner.ScannerClient {
|
||||
|
||||
@Context
|
||||
ServletContext servletContext;
|
||||
|
||||
LinkedBlockingQueue<ScanCallback> callbacks;
|
||||
|
||||
/**
|
||||
* This method is called by the Jetty engine when instantiating the servlet
|
||||
*/
|
||||
@PostConstruct
|
||||
@SuppressWarnings("unchecked")
|
||||
public void init() throws Exception {
|
||||
Object context = servletContext.getAttribute(ReceiverScanHandler.CALLBACKS_ATTRIBUTE_NAME);
|
||||
|
||||
try {
|
||||
callbacks = (LinkedBlockingQueue<ScanCallback>) context;
|
||||
} catch (ClassCastException e) {
|
||||
throw e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@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
|
||||
@Path(POLLING_STATION_WEB_SCANNER_SCAN_PATH)
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BoolValue newScan(PollingStation.ScannedData scannedData, Crypto.Signature scannedDataSignature) {
|
||||
|
||||
boolean sent = false;
|
||||
|
||||
// for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
|
||||
//
|
||||
// callback.onSuccess(scannedData);
|
||||
// handled = true;
|
||||
//
|
||||
// }
|
||||
try {
|
||||
ScanDataCallback callback = new ScanDataCallback(scannedData);
|
||||
// scanner.subscribe(callback);
|
||||
callbacks.add(callback);
|
||||
sent = true;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return BoolValue.newBuilder()
|
||||
.setValue(sent)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path(POLLING_STATION_WEB_SCANNER_ERROR_PATH)
|
||||
@Consumes(MEDIATYPE_PROTOBUF)
|
||||
@Produces(MEDIATYPE_PROTOBUF)
|
||||
@Override
|
||||
public BoolValue reportScanError(PollingStation.ErrorMsg errorMsg, Crypto.Signature errorMsgSignature) {
|
||||
|
||||
boolean handled = false;
|
||||
|
||||
// for (FutureCallback<PollingStation.ScannedData> callback : callbacks){
|
||||
//
|
||||
// callback.onFailure(new IOException(errorMsg.getMsg()));
|
||||
// handled = true;
|
||||
//
|
||||
// }
|
||||
|
||||
ScanErrorCallback callback = new ScanErrorCallback(errorMsg);
|
||||
// scanner.subscribe(callback);
|
||||
callbacks.add(callback);
|
||||
|
||||
return BoolValue.newBuilder()
|
||||
.setValue(handled)
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
package meerkat.pollingstation.controller.callbacks;
|
||||
|
||||
/**
|
||||
* Created by Laura on 3/20/2017.
|
||||
*/
|
||||
public interface ScanCallback {
|
||||
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
package meerkat.pollingstation.controller.callbacks;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
|
||||
/**
|
||||
* Created by Laura on 3/20/2017.
|
||||
*/
|
||||
public class ScanDataCallback implements ScanCallback, FutureCallback<PollingStation.ScannedData> {
|
||||
|
||||
private final PollingStation.ScannedData expectedData;
|
||||
private boolean dataIsAsExpected;
|
||||
private Throwable thrown;
|
||||
|
||||
public ScanDataCallback(PollingStation.ScannedData expectedData) {
|
||||
this.expectedData = expectedData;
|
||||
}
|
||||
|
||||
public PollingStation.ScannedData getScannedData() {
|
||||
return this.expectedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(PollingStation.ScannedData result) {
|
||||
|
||||
dataIsAsExpected = result.getBallot().getChannel().equals(expectedData.getBallot().getChannel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
dataIsAsExpected = false;
|
||||
thrown = t;
|
||||
}
|
||||
|
||||
public void doNothing() {
|
||||
System.out.println("do nothing hit");
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
package meerkat.pollingstation.controller.callbacks;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
|
||||
/**
|
||||
* Created by Laura on 3/20/2017.
|
||||
*/
|
||||
public class ScanErrorCallback extends ScanCallback implements FutureCallback<PollingStation.ErrorMsg> {
|
||||
|
||||
private final PollingStation.ErrorMsg expectedErrorMessage;
|
||||
|
||||
public ScanErrorCallback(PollingStation.ErrorMsg expectedErrorMessage) {
|
||||
this.expectedErrorMessage = expectedErrorMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(PollingStation.ErrorMsg msg) {
|
||||
// dataIsAsExpected = false;
|
||||
// semaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
// dataIsAsExpected = t.getMessage().equals(expectedErrorMessage);
|
||||
// semaphore.release();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
package meerkat.pollingstation;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.crypto.DigitalSignatureGenerator;
|
||||
import meerkat.crypto.concrete.ECDSADeterministicSignature;
|
||||
import meerkat.protobuf.Crypto;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
import meerkat.protobuf.PollingStation.ScannedData;
|
||||
import meerkat.protobuf.Voting;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ArrayBlockingQueue;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
public class PollingStationScannerTest {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private PollingStationScanner.PollingStationServer scanner;
|
||||
private static final String CONTEXT_PATH = "/scan";
|
||||
|
||||
/**
|
||||
* Waiting time for transmission in ms.
|
||||
*/
|
||||
final static long TIMEOUT = 500;
|
||||
|
||||
PollingStation.ConnectionServerData serverData = null;
|
||||
DigitalSignatureGenerator clientSigner;
|
||||
|
||||
|
||||
BlockingQueue<ScannedData> results = new ArrayBlockingQueue<ScannedData>(1);
|
||||
|
||||
|
||||
private Throwable thrown;
|
||||
|
||||
private class ScanHandler implements FutureCallback<ScannedData> {
|
||||
@Override
|
||||
public void onSuccess(ScannedData result) {
|
||||
results.add(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
thrown = t;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
clientSigner = new ECDSADeterministicSignature();
|
||||
clientSigner.generateSigningCertificate(BigInteger.ONE, new Date(System.currentTimeMillis()),
|
||||
new Date(System.currentTimeMillis() + 1000*60*60*24*365), "testing");
|
||||
|
||||
logger.debug("Setting up Scanner WebApp!");
|
||||
|
||||
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
|
||||
scanner.subscribe(new ScanHandler());
|
||||
|
||||
thrown = null;
|
||||
|
||||
try {
|
||||
serverData = scanner.start(true);
|
||||
} catch (Exception e) {
|
||||
fail("Could not start server: " + e.getMessage());
|
||||
}
|
||||
|
||||
logger.info("Started scanner web service. API endpoint: {}", serverData.getServerUrl());
|
||||
|
||||
}
|
||||
|
||||
PollingStationScanner.ScannerClient connectClient() throws InterruptedException {
|
||||
|
||||
PollingStationScanner.ScannerClient scannerClient =
|
||||
new ScannerClientAPI(clientSigner);
|
||||
|
||||
boolean connectOk = scannerClient.connect(serverData);
|
||||
|
||||
assertTrue("Connection to Scanner server failed!", connectOk);
|
||||
|
||||
return scannerClient;
|
||||
}
|
||||
|
||||
|
||||
void checkResult(PollingStation.ScannedBallot ballot, long timeout) throws Exception {
|
||||
ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS);
|
||||
assertEquals("Expecting ballot in received scan", ScannedData.DataCase.BALLOT, arrivedData.getDataCase());
|
||||
assertEquals("Recieved scan does not match transmission", ballot, arrivedData.getBallot());
|
||||
}
|
||||
|
||||
void checkResult(PollingStation.ScanError error, long timeout) throws Exception {
|
||||
ScannedData arrivedData = results.poll(timeout, TimeUnit.MILLISECONDS);
|
||||
assertEquals("Expecting error in received scan", ScannedData.DataCase.ERROR, arrivedData.getDataCase());
|
||||
assertEquals("Recieved scan does not match transmission", error, arrivedData.getError());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSuccessfulScan() throws Exception {
|
||||
|
||||
byte[] data = {(byte) 1, (byte) 2};
|
||||
|
||||
PollingStation.ScannedBallot ballot = PollingStation.ScannedBallot.newBuilder()
|
||||
.setChannel(ByteString.copyFrom(data))
|
||||
.setSignedEncryptedBallot(Voting.SignedEncryptedBallot.newBuilder()
|
||||
.setEncryptedBallot(Voting.EncryptedBallot.newBuilder()
|
||||
.setSerialNumber(0)
|
||||
.setData(Crypto.RerandomizableEncryptedMessage.newBuilder()
|
||||
.setData(ByteString.copyFromUtf8("Testing"))
|
||||
)
|
||||
)
|
||||
)
|
||||
.build();
|
||||
|
||||
PollingStationScanner.ScannerClient client = connectClient();
|
||||
|
||||
boolean scanOk = client.newScan(ballot);
|
||||
|
||||
assertTrue("Scan transmission failed", scanOk);
|
||||
|
||||
checkResult(ballot, TIMEOUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErroneousScan() throws Exception {
|
||||
PollingStation.ScanError errorMsg = PollingStation.ScanError.newBuilder()
|
||||
.setMsg("!Error Message!")
|
||||
.build();
|
||||
|
||||
PollingStationScanner.ScannerClient client = connectClient();
|
||||
boolean scanOk = client.reportError(errorMsg);
|
||||
assertTrue("Error report transmission failed", scanOk);
|
||||
|
||||
checkResult(errorMsg, TIMEOUT);
|
||||
}
|
||||
|
||||
@After
|
||||
public void shutDown() {
|
||||
logger.debug("{} shutting down...", scanner.getClass().getCanonicalName());
|
||||
|
||||
try {
|
||||
scanner.stop();
|
||||
} catch (Exception e) {
|
||||
fail("Could not stop server: " + e.getMessage());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
package meerkat.pollingstation;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.PollingStation.*;
|
||||
import meerkat.rest.Constants;
|
||||
|
||||
import meerkat.rest.*;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static meerkat.pollingstation.PollingStationConstants.*;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* Created by Arbel on 25/05/2016.
|
||||
*/
|
||||
public class PollingStationWebScannerTest {
|
||||
|
||||
private PollingStationScanner.PollingStationServer scanner;
|
||||
private static final String ADDRESS = "http://localhost";
|
||||
private static final String SUB_ADDRESS = "";
|
||||
private static final int PORT = 8080;
|
||||
|
||||
private Semaphore semaphore;
|
||||
private Throwable thrown;
|
||||
private boolean dataIsAsExpected;
|
||||
|
||||
private class ScanHandler implements FutureCallback<ScannedData> {
|
||||
|
||||
private final ScannedData expectedData;
|
||||
|
||||
public ScanHandler(ScannedData expectedData) {
|
||||
this.expectedData = expectedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(ScannedData result) {
|
||||
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
|
||||
semaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
dataIsAsExpected = false;
|
||||
thrown = t;
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
public void init() {
|
||||
|
||||
System.err.println("Setting up Scanner WebApp!");
|
||||
|
||||
scanner = new PollingStationWebScanner(PORT, SUB_ADDRESS);
|
||||
|
||||
semaphore = new Semaphore(0);
|
||||
thrown = null;
|
||||
|
||||
try {
|
||||
scanner.start();
|
||||
} catch (Exception e) {
|
||||
assertThat("Could not start server: " + e.getMessage(), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulScan() throws InterruptedException {
|
||||
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
|
||||
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH);
|
||||
|
||||
byte[] data = {(byte) 1, (byte) 2};
|
||||
|
||||
ScannedData scannedData = ScannedData.newBuilder()
|
||||
.setChannel(ByteString.copyFrom(data))
|
||||
.build();
|
||||
|
||||
scanner.subscribe(new ScanHandler(scannedData));
|
||||
|
||||
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
|
||||
response.close();
|
||||
|
||||
semaphore.acquire();
|
||||
assertThat("Scanner has thrown an error", thrown == null);
|
||||
assertThat("Scanned data received was incorrect", dataIsAsExpected);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErroneousScan() throws InterruptedException {
|
||||
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
|
||||
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_ERROR_PATH);
|
||||
|
||||
ErrorMsg errorMsg = ErrorMsg.newBuilder()
|
||||
.setMsg("!Error Message!")
|
||||
.build();
|
||||
|
||||
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
|
||||
|
||||
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF));
|
||||
response.close();
|
||||
|
||||
semaphore.acquire();
|
||||
assertThat("Scanner error received was incorrect", dataIsAsExpected);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
|
||||
System.err.println("Scanner WebApp shutting down...");
|
||||
|
||||
try {
|
||||
scanner.stop();
|
||||
} catch (Exception e) {
|
||||
assertThat("Could not stop server: " + e.getMessage(), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,162 +0,0 @@
|
|||
package meerkat.pollingstation;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.PollingStation.ErrorMsg;
|
||||
import meerkat.protobuf.PollingStation.ScannedData;
|
||||
import meerkat.rest.Constants;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
import meerkat.rest.ProtobufMessageBodyWriter;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.client.Entity;
|
||||
import javax.ws.rs.client.WebTarget;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
|
||||
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
/**
|
||||
* Created by Arbel on 25/05/2016.
|
||||
*/
|
||||
public class ReceiverTest {
|
||||
|
||||
private PollingStationScanner.PollingStationServer scanner;
|
||||
private static final String ADDRESS = "http://localhost";
|
||||
private static final String SUB_ADDRESS = "";
|
||||
private static final int PORT = 8080;
|
||||
|
||||
private Semaphore semaphore;
|
||||
private Throwable thrown;
|
||||
private boolean dataIsAsExpected;
|
||||
|
||||
private class ScanHandler implements FutureCallback<ScannedData> {
|
||||
|
||||
private final ScannedData expectedData;
|
||||
|
||||
public ScanHandler(ScannedData expectedData) {
|
||||
this.expectedData = expectedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(ScannedData result) {
|
||||
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
|
||||
semaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
dataIsAsExpected = false;
|
||||
thrown = t;
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
public void init() {
|
||||
|
||||
System.err.println("Setting up Scanner WebApp!");
|
||||
|
||||
scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
|
||||
|
||||
semaphore = new Semaphore(0);
|
||||
thrown = null;
|
||||
|
||||
try {
|
||||
scanner.start();
|
||||
} catch (Exception e) {
|
||||
assertThat("Could not start server: " + e.getMessage(), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulScan() throws InterruptedException {
|
||||
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
|
||||
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_SCAN_PATH);
|
||||
|
||||
byte[] data = {(byte) 1, (byte) 2};
|
||||
|
||||
ScannedData scannedData = ScannedData.newBuilder()
|
||||
.setChannel(ByteString.copyFrom(data))
|
||||
.build();
|
||||
|
||||
scanner.subscribe(new ScanHandler(scannedData));
|
||||
|
||||
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(scannedData, Constants.MEDIATYPE_PROTOBUF));
|
||||
response.close();
|
||||
|
||||
semaphore.acquire();
|
||||
assertThat("Scanner has thrown an error", thrown == null);
|
||||
assertThat("Scanned data received was incorrect", dataIsAsExpected);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErroneousScan() throws InterruptedException {
|
||||
|
||||
Client client = ClientBuilder.newClient();
|
||||
client.register(ProtobufMessageBodyReader.class);
|
||||
client.register(ProtobufMessageBodyWriter.class);
|
||||
WebTarget webTarget = client.target(ADDRESS + ":" + PORT)
|
||||
.path(SUB_ADDRESS).path(POLLING_STATION_WEB_SCANNER_ERROR_PATH);
|
||||
|
||||
ErrorMsg errorMsg = ErrorMsg.newBuilder()
|
||||
.setMsg("!Error Message!")
|
||||
.build();
|
||||
|
||||
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
|
||||
|
||||
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(errorMsg, Constants.MEDIATYPE_PROTOBUF));
|
||||
response.close();
|
||||
|
||||
semaphore.acquire();
|
||||
assertThat("Scanner error received was incorrect", dataIsAsExpected);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
|
||||
System.err.println("ReceiverScanHandler shutting down...");
|
||||
|
||||
try {
|
||||
scanner.stop();
|
||||
} catch (Exception e) {
|
||||
assertThat("Could not stop server: " + e.getMessage(), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
package meerkat.pollingstation;
|
||||
|
||||
import com.google.common.util.concurrent.FutureCallback;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
import meerkat.protobuf.PollingStation.ErrorMsg;
|
||||
import meerkat.protobuf.PollingStation.ScannedData;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_ERROR_PATH;
|
||||
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
|
||||
|
||||
public class Receiver_ClientTest {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
private PollingStationScanner.PollingStationServer scanner;
|
||||
private static final String CONTEXT_PATH = "/scan";
|
||||
|
||||
private Semaphore semaphore;
|
||||
private Throwable thrown;
|
||||
private boolean dataIsAsExpected;
|
||||
|
||||
PollingStation.ConnectionServerData serverData = null;
|
||||
|
||||
private class ScanHandler implements FutureCallback<ScannedData> {
|
||||
|
||||
private final ScannedData expectedData;
|
||||
|
||||
public ScanHandler(ScannedData expectedData) {
|
||||
this.expectedData = expectedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(ScannedData result) {
|
||||
dataIsAsExpected = result.getChannel().equals(expectedData.getChannel());
|
||||
semaphore.release();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
dataIsAsExpected = false;
|
||||
thrown = t;
|
||||
semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
|
||||
System.err.println("Setting up Scanner WebApp!");
|
||||
|
||||
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
|
||||
|
||||
semaphore = new Semaphore(0);
|
||||
thrown = null;
|
||||
|
||||
PollingStation.ConnectionServerData serverData = null;
|
||||
try {
|
||||
serverData = scanner.start(false);
|
||||
} catch (Exception e) {
|
||||
assertThat("Could not start server: " + e.getMessage(), false);
|
||||
}
|
||||
|
||||
logger.info("Started scanner web service. API endpoint: {}", serverData.getServerUrl());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulScan() throws InterruptedException {
|
||||
|
||||
byte[] data = {(byte) 1, (byte) 2};
|
||||
|
||||
ScannedData scannedData = ScannedData.newBuilder()
|
||||
.setBallot(PollingStation.ScannedBallot.newBuilder()
|
||||
.setChannel(ByteString.copyFrom(data))
|
||||
)
|
||||
.build();
|
||||
|
||||
scanner.subscribe(new ScanHandler(scannedData));
|
||||
|
||||
PollingStationScanner.ScannerClient scannerClient = new ScannerClientAPI()
|
||||
|
||||
ScannerClientAPI(PSC_URL + POLLING_STATION_WEB_SCANNER_SCAN_PATH);
|
||||
scannerClient.newScan(scannedData, null);
|
||||
|
||||
semaphore.acquire();
|
||||
assertThat("Scanner has thrown an error", thrown == null);
|
||||
assertThat("Scanned data received was incorrect", dataIsAsExpected);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testErroneousScan() throws InterruptedException {
|
||||
|
||||
ErrorMsg errorMsg = ErrorMsg.newBuilder()
|
||||
.setMsg("!Error Message!")
|
||||
.build();
|
||||
|
||||
scanner.subscribe(new ErrorHandler(errorMsg.getMsg()));
|
||||
|
||||
ScannerClientAPI scannerClient = new ScannerClientAPI();
|
||||
|
||||
|
||||
PSC_URL + POLLING_STATION_WEB_SCANNER_ERROR_PATH);
|
||||
scannerClient.sendError(errorMsg);
|
||||
|
||||
semaphore.acquire();
|
||||
assertThat("Scanner error received was incorrect", dataIsAsExpected);
|
||||
|
||||
}
|
||||
|
||||
@After
|
||||
public void close() {
|
||||
|
||||
System.err.println("ReceiverScanHandler shutting down...");
|
||||
|
||||
try {
|
||||
scanner.stop();
|
||||
} catch (Exception e) {
|
||||
assertThat("Could not stop server: " + e.getMessage(), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -7,8 +7,7 @@ public interface PollingStationConstants {
|
|||
|
||||
// Relative addresses for Scanner operations
|
||||
|
||||
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_SCAN_PATH = "/data";
|
||||
public static final String POLLING_STATION_WEB_SCANNER_CONNECT_PATH = "/connect";
|
||||
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ package meerkat.pollingstation;
|
|||
|
||||
import com.google.protobuf.BoolValue;
|
||||
import com.google.protobuf.ByteString;
|
||||
import meerkat.crypto.DigitalSignatureGenerator;
|
||||
import meerkat.crypto.DigitalSignature;
|
||||
import meerkat.protobuf.PollingStation;
|
||||
import meerkat.rest.Constants;
|
||||
import meerkat.rest.ProtobufMessageBodyReader;
|
||||
|
@ -17,16 +17,19 @@ import javax.ws.rs.client.WebTarget;
|
|||
import javax.ws.rs.core.Response;
|
||||
import java.security.SignatureException;
|
||||
|
||||
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_CONNECT_PATH;
|
||||
import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB_SCANNER_SCAN_PATH;
|
||||
|
||||
/**
|
||||
* Created by Laura on 3/20/2017.
|
||||
*/
|
||||
public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
DigitalSignatureGenerator signer;
|
||||
DigitalSignature signer;
|
||||
ByteString scannerID;
|
||||
Client client;
|
||||
WebTarget webTarget;
|
||||
WebTarget baseWebTarget;
|
||||
|
||||
/**
|
||||
* Serial number of request (zeroed on new connection to server)
|
||||
|
@ -55,7 +58,7 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
|||
* Constructor for client that signs messages.
|
||||
* @param signer
|
||||
*/
|
||||
public ScannerClientAPI(DigitalSignatureGenerator signer) {
|
||||
public ScannerClientAPI(DigitalSignature signer) {
|
||||
this();
|
||||
assert (signer != null && signer.getSignerID() != null); // Use the default constructor instead
|
||||
|
||||
|
@ -71,7 +74,7 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
|||
@Override
|
||||
public boolean connect(PollingStation.ConnectionServerData serverData) {
|
||||
serial = 0;
|
||||
webTarget = client.target(serverData.getServerUrl());
|
||||
baseWebTarget = client.target(serverData.getServerUrl());
|
||||
PollingStation.ConnectionClientData.Builder clientData = PollingStation.ConnectionClientData.newBuilder()
|
||||
.setNonce(serverData.getNonce());
|
||||
|
||||
|
@ -81,7 +84,8 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
|||
clientData.setScannerId(scannerID);
|
||||
}
|
||||
|
||||
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
|
||||
Response response = baseWebTarget.path(POLLING_STATION_WEB_SCANNER_CONNECT_PATH)
|
||||
.request(Constants.MEDIATYPE_PROTOBUF)
|
||||
.post(Entity.entity(clientData.build(), Constants.MEDIATYPE_PROTOBUF));
|
||||
BoolValue res = response.readEntity(BoolValue.class);
|
||||
response.close();
|
||||
|
@ -106,7 +110,8 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
|||
}
|
||||
}
|
||||
|
||||
Response response = webTarget.request(Constants.MEDIATYPE_PROTOBUF)
|
||||
Response response = baseWebTarget.path(POLLING_STATION_WEB_SCANNER_SCAN_PATH)
|
||||
.request(Constants.MEDIATYPE_PROTOBUF)
|
||||
.post(Entity.entity(signedDataBuilder.build(), Constants.MEDIATYPE_PROTOBUF));
|
||||
BoolValue res = response.readEntity(BoolValue.class);
|
||||
response.close();
|
||||
|
|
Loading…
Reference in New Issue