Mostly finished with scanAPI refactoring
parent
cb1d2343c4
commit
6f35f105c5
|
@ -53,6 +53,11 @@ public class GenericBulletinBoardSignature implements BulletinBoardSignature {
|
||||||
signer.loadVerificationCertificate(cert);
|
signer.loadVerificationCertificate(cert);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteString computeCertificateFingerprint(Crypto.SignatureVerificationKey cert) throws CertificateException {
|
||||||
|
return signer.computeCertificateFingerprint(cert);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
|
public void loadVerificationCertificates(InputStream certStream) throws CertificateException {
|
||||||
signer.loadVerificationCertificates(certStream);
|
signer.loadVerificationCertificates(certStream);
|
||||||
|
|
|
@ -34,6 +34,13 @@ public interface DigitalSignature {
|
||||||
throws CertificateException;
|
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.
|
* Load a set of certificates from an input stream.
|
||||||
* This will consume the entire stream.
|
* This will consume the entire stream.
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.io.InputStream;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
import java.security.cert.*;
|
import java.security.cert.*;
|
||||||
|
import java.security.cert.Certificate;
|
||||||
import java.security.interfaces.ECPrivateKey;
|
import java.security.interfaces.ECPrivateKey;
|
||||||
|
|
||||||
|
|
||||||
|
@ -68,26 +69,21 @@ public class ECDSADeterministicSignature extends ECDSASignature {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadSigningCertificate(KeyStore.Builder keyStoreBuilder)
|
public void loadSigningCertificate(Certificate cert, PrivateKey key) throws InvalidKeyException, CertificateException {
|
||||||
throws CertificateException, UnrecoverableKeyException, IOException {
|
super.loadSigningCertificate(cert, key);
|
||||||
super.loadSigningCertificate(keyStoreBuilder);
|
|
||||||
|
|
||||||
if (!(loadedSigningKey instanceof ECPrivateKey)) {
|
if (!(loadedSigningKey instanceof ECPrivateKey)) {
|
||||||
logger.error("Wrong private key type (expected ECPrivateKey, got {})", loadedSigningKey.getClass());
|
logger.error("Wrong private key type (expected ECPrivateKey, got {})", loadedSigningKey.getClass());
|
||||||
throw new CertificateException("Wrong signing key type!");
|
throw new CertificateException("Wrong signing key type!");
|
||||||
}
|
}
|
||||||
ECPrivateKey key = (ECPrivateKey) loadedSigningKey;
|
ECPrivateKey eckey = (ECPrivateKey) loadedSigningKey;
|
||||||
|
|
||||||
AsymmetricKeyParameter baseParams;
|
AsymmetricKeyParameter baseParams;
|
||||||
try {
|
baseParams = ECUtil.generatePrivateKeyParameter(eckey);
|
||||||
baseParams = ECUtil.generatePrivateKeyParameter(key);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new UnrecoverableKeyException("Couldn't convert private key");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(baseParams instanceof ECPrivateKeyParameters)) {
|
if (!(baseParams instanceof ECPrivateKeyParameters)) {
|
||||||
logger.error("Error converting to bouncycastle type! (got {})", baseParams.getClass());
|
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;
|
ECPrivateKeyParameters params = (ECPrivateKeyParameters) baseParams;
|
||||||
|
|
|
@ -135,11 +135,33 @@ public class ECDSASignature implements DigitalSignatureGenerator {
|
||||||
loadVerificationCertificates(certStream);
|
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
|
@Override
|
||||||
public void loadVerificationCertificates(InputStream certStream)
|
public void loadVerificationCertificates(InputStream certStream)
|
||||||
throws CertificateException {
|
throws CertificateException {
|
||||||
CertificateFactory certificateFactory = CertificateFactory.getInstance(CERTIFICATE_ENCODING_X509);
|
Collection<? extends Certificate> certs = deserializeCertificates(certStream);
|
||||||
Collection<? extends Certificate> certs = certificateFactory.generateCertificates(certStream);
|
|
||||||
for (Certificate cert : certs) {
|
for (Certificate cert : certs) {
|
||||||
loadVerificationCertificate(cert);
|
loadVerificationCertificate(cert);
|
||||||
}
|
}
|
||||||
|
@ -321,6 +343,8 @@ public class ECDSASignature implements DigitalSignatureGenerator {
|
||||||
}
|
}
|
||||||
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
|
logger.trace("keystore entry {}, has key type {}", alias, key.getClass());
|
||||||
if (key instanceof PrivateKey) {
|
if (key instanceof PrivateKey) {
|
||||||
|
|
||||||
|
// Here is where we *actually* load the private key
|
||||||
loadSigningCertificate(cert, (PrivateKey) key);
|
loadSigningCertificate(cert, (PrivateKey) key);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -11,16 +11,13 @@ import static meerkat.pollingstation.PollingStationConstants.POLLING_STATION_WEB
|
||||||
* Created by Laura on 3/20/2017.
|
* Created by Laura on 3/20/2017.
|
||||||
*/
|
*/
|
||||||
public class PollingStationClientToyRun {
|
public class PollingStationClientToyRun {
|
||||||
|
|
||||||
private static final String PSC_URL = "http://192.168.70.10:8880/";
|
|
||||||
|
|
||||||
public static void main(String [] args) {
|
public static void main(String [] args) {
|
||||||
int channel = 10;
|
int channel = 10;
|
||||||
System.out.println("Message to send: "+channel);
|
System.out.println("Message to send: "+channel);
|
||||||
byte[] data = ByteBuffer.allocate(4).putInt(channel).array();
|
byte[] data = ByteBuffer.allocate(4).putInt(channel).array();
|
||||||
// byte[] data = {(byte) 1, (byte) 2};
|
// byte[] data = {(byte) 1, (byte) 2};
|
||||||
|
|
||||||
PollingStation.ScannedData scannedData = PollingStation.ScannedData.newBuilder()
|
PollingStation.ScannedBallot scannedBallot = PollingStation.ScannedBallot.newBuilder()
|
||||||
.setChannel(ByteString.copyFrom(data))
|
.setChannel(ByteString.copyFrom(data))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
@ -29,9 +26,10 @@ public class PollingStationClientToyRun {
|
||||||
// System.out.println("Channel int initial:"+channel);
|
// System.out.println("Channel int initial:"+channel);
|
||||||
// System.out.println("Channel int retrieved:"+retrieved);
|
// System.out.println("Channel int retrieved:"+retrieved);
|
||||||
|
|
||||||
ScannerClientAPI scannerClient = new ScannerClientAPI(PSC_URL);
|
PollingStationScanner.ScannerClient scannerClient = new ScannerClientAPI();
|
||||||
boolean sent = scannerClient.sendScan(scannedData);
|
|
||||||
|
|
||||||
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.
|
* Created by Arbel on 5/31/2016.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
|
||||||
import com.google.protobuf.BoolValue;
|
import com.google.protobuf.BoolValue;
|
||||||
import meerkat.crypto.DigitalSignature;
|
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
|
||||||
import meerkat.protobuf.PollingStation;
|
import meerkat.protobuf.PollingStation;
|
||||||
|
|
||||||
import javax.annotation.PostConstruct;
|
import javax.annotation.PostConstruct;
|
||||||
|
@ -43,7 +40,7 @@ public class PollingStationScannerWebApp implements PollingStationScanner.Pollin
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void init() throws Exception {
|
public void init() throws Exception {
|
||||||
Object context = servletContext.getAttribute(PollingStationWebScanner.CALLBACKS_ATTRIBUTE_NAME);
|
Object context = servletContext.getAttribute(PollingStationWebScanner.SCAN_HANDLER_ATTRIBUTE_NAME);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
requestHandler = (PollingStationWebScanner.ScanRequestHandler) context;
|
requestHandler = (PollingStationWebScanner.ScanRequestHandler) context;
|
||||||
|
|
|
@ -8,14 +8,14 @@ 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());
|
final static Logger logger = LoggerFactory.getLogger(PollingStation.class);
|
||||||
|
|
||||||
private static PollingStationScanner.PollingStationServer scanner;
|
private static PollingStationScanner.PollingStationServer scanner;
|
||||||
private static final String CONTEXT_PATH = "/scan";
|
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 {
|
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);
|
// scanner = new ReceiverScanHandler(PORT, SUB_ADDRESS);
|
||||||
//
|
//
|
||||||
|
@ -27,19 +27,12 @@ public class PollingStationToyRun {
|
||||||
// }
|
// }
|
||||||
|
|
||||||
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
|
scanner = new PollingStationWebScanner(0, CONTEXT_PATH);
|
||||||
PollingStation.ConnectionServerData serverData = scanner.start(false);
|
PollingStation.ConnectionServerData serverData = scanner.start(true);
|
||||||
|
|
||||||
|
|
||||||
PollingStationMainController controller = new PollingStationMainController();
|
PollingStationMainController controller = new PollingStationMainController();
|
||||||
controller.init(scanner);
|
controller.init(scanner);
|
||||||
|
|
||||||
Thread controllerThread = new Thread(controller);
|
Thread.currentThread().setName("Meerkat PS-Controller Thread");
|
||||||
controllerThread.setName("Meerkat PS-Controller Thread");
|
controller.run();
|
||||||
// Thread serverThread = new Thread(serverController);
|
|
||||||
// serverThread.setName("Meerkat PS-ServerScanner Thread");
|
|
||||||
|
|
||||||
controllerThread.start();
|
|
||||||
serverThread.start();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import meerkat.crypto.DigitalSignature;
|
import meerkat.crypto.DigitalSignature;
|
||||||
import meerkat.crypto.concrete.ECDSASignature;
|
import meerkat.crypto.concrete.ECDSASignature;
|
||||||
|
import meerkat.protobuf.Crypto;
|
||||||
import meerkat.protobuf.PollingStation;
|
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.*;
|
||||||
|
@ -28,7 +29,7 @@ import org.slf4j.LoggerFactory;
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class PollingStationWebScanner implements PollingStationScanner.PollingStationServer {
|
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());
|
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
|
@ -59,6 +60,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
DigitalSignature verifier;
|
DigitalSignature verifier;
|
||||||
|
ByteString connectedScannerID;
|
||||||
|
|
||||||
private final Server server;
|
private final Server server;
|
||||||
|
|
||||||
|
@ -75,17 +77,35 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
if (verifySignatures) {
|
||||||
|
|
||||||
if (verifySignatures) {
|
|
||||||
try {
|
|
||||||
if (data.getIdCase() != PollingStation.ConnectionClientData.IdCase.SCANNERPK)
|
if (data.getIdCase() != PollingStation.ConnectionClientData.IdCase.SCANNERPK)
|
||||||
throw new CertificateException("Connection request doesn't include certificate!");
|
throw new CertificateException("Connection request doesn't include certificate!");
|
||||||
|
|
||||||
verifier.loadVerificationCertificate(data.getScannerPK());
|
Crypto.SignatureVerificationKey pk = data.getScannerPK();
|
||||||
} catch (CertificateException e) {
|
connectedScannerID = verifier.computeCertificateFingerprint(pk);
|
||||||
logger.warn("Scanner connection with bad certificate: {}", e);
|
verifier.loadVerificationCertificate(pk);
|
||||||
return false;
|
|
||||||
|
} 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;
|
return true;
|
||||||
|
@ -95,7 +115,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
||||||
ScannedData scannedData = data.getData();
|
ScannedData scannedData = data.getData();
|
||||||
if (verifySignatures) {
|
if (verifySignatures) {
|
||||||
ByteString scannerID = scannedData.getScannerId();
|
ByteString scannerID = scannedData.getScannerId();
|
||||||
if (!scannerID.equals(verifier.getSignerID())) {
|
if (!scannerID.equals(connectedScannerID)) {
|
||||||
logger.warn("Scanner ID doesn't match connection public key");
|
logger.warn("Scanner ID doesn't match connection public key");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -141,7 +161,7 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
||||||
server = new Server(port);
|
server = new Server(port);
|
||||||
|
|
||||||
ServletContextHandler servletContextHandler = new ServletContextHandler(server, contextPath);
|
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 resourceConfig = new ResourceConfig(PollingStationScannerWebApp.class);
|
||||||
resourceConfig.register(ProtobufMessageBodyReader.class);
|
resourceConfig.register(ProtobufMessageBodyReader.class);
|
||||||
|
@ -153,14 +173,16 @@ public class PollingStationWebScanner implements PollingStationScanner.PollingSt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
|
public PollingStation.ConnectionServerData start(boolean verifyScanner) throws Exception {
|
||||||
this.verifyNonce = this.verifySignatures = verifyScanner;
|
this.verifyNonce = this.verifySignatures = verifyScanner;
|
||||||
nonce = new SecureRandom().nextLong();
|
nonce = new SecureRandom().nextLong();
|
||||||
|
|
||||||
server.start();
|
server.start();
|
||||||
return null;
|
return PollingStation.ConnectionServerData.newBuilder()
|
||||||
|
.setNonce(nonce)
|
||||||
|
.setServerUrl(server.getURI().toString())
|
||||||
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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
|
// Relative addresses for Scanner operations
|
||||||
|
|
||||||
public static final String POLLING_STATION_WEB_SCANNER_SCAN_PATH = "/scan";
|
public static final String POLLING_STATION_WEB_SCANNER_SCAN_PATH = "/data";
|
||||||
public static final String POLLING_STATION_WEB_SCANNER_ERROR_PATH = "/error";
|
|
||||||
public static final String POLLING_STATION_WEB_SCANNER_CONNECT_PATH = "/connect";
|
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.BoolValue;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import meerkat.crypto.DigitalSignatureGenerator;
|
import meerkat.crypto.DigitalSignature;
|
||||||
import meerkat.protobuf.PollingStation;
|
import meerkat.protobuf.PollingStation;
|
||||||
import meerkat.rest.Constants;
|
import meerkat.rest.Constants;
|
||||||
import meerkat.rest.ProtobufMessageBodyReader;
|
import meerkat.rest.ProtobufMessageBodyReader;
|
||||||
|
@ -17,16 +17,19 @@ import javax.ws.rs.client.WebTarget;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.security.SignatureException;
|
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.
|
* Created by Laura on 3/20/2017.
|
||||||
*/
|
*/
|
||||||
public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
||||||
final Logger logger = LoggerFactory.getLogger(getClass());
|
final Logger logger = LoggerFactory.getLogger(getClass());
|
||||||
|
|
||||||
DigitalSignatureGenerator signer;
|
DigitalSignature signer;
|
||||||
ByteString scannerID;
|
ByteString scannerID;
|
||||||
Client client;
|
Client client;
|
||||||
WebTarget webTarget;
|
WebTarget baseWebTarget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Serial number of request (zeroed on new connection to server)
|
* 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.
|
* Constructor for client that signs messages.
|
||||||
* @param signer
|
* @param signer
|
||||||
*/
|
*/
|
||||||
public ScannerClientAPI(DigitalSignatureGenerator signer) {
|
public ScannerClientAPI(DigitalSignature signer) {
|
||||||
this();
|
this();
|
||||||
assert (signer != null && signer.getSignerID() != null); // Use the default constructor instead
|
assert (signer != null && signer.getSignerID() != null); // Use the default constructor instead
|
||||||
|
|
||||||
|
@ -71,7 +74,7 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
||||||
@Override
|
@Override
|
||||||
public boolean connect(PollingStation.ConnectionServerData serverData) {
|
public boolean connect(PollingStation.ConnectionServerData serverData) {
|
||||||
serial = 0;
|
serial = 0;
|
||||||
webTarget = client.target(serverData.getServerUrl());
|
baseWebTarget = client.target(serverData.getServerUrl());
|
||||||
PollingStation.ConnectionClientData.Builder clientData = PollingStation.ConnectionClientData.newBuilder()
|
PollingStation.ConnectionClientData.Builder clientData = PollingStation.ConnectionClientData.newBuilder()
|
||||||
.setNonce(serverData.getNonce());
|
.setNonce(serverData.getNonce());
|
||||||
|
|
||||||
|
@ -81,7 +84,8 @@ public class ScannerClientAPI implements PollingStationScanner.ScannerClient {
|
||||||
clientData.setScannerId(scannerID);
|
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));
|
.post(Entity.entity(clientData.build(), Constants.MEDIATYPE_PROTOBUF));
|
||||||
BoolValue res = response.readEntity(BoolValue.class);
|
BoolValue res = response.readEntity(BoolValue.class);
|
||||||
response.close();
|
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));
|
.post(Entity.entity(signedDataBuilder.build(), Constants.MEDIATYPE_PROTOBUF));
|
||||||
BoolValue res = response.readEntity(BoolValue.class);
|
BoolValue res = response.readEntity(BoolValue.class);
|
||||||
response.close();
|
response.close();
|
||||||
|
|
Loading…
Reference in New Issue