Added filters and DB reading functionality.

Also added signer IDs and multiple signatures (on same message) to schema.
Bulletin_Board_Server_phase_1
Arbel Deutsch Peled 2015-11-20 21:40:45 +02:00
parent 47edf0df34
commit a6afb74893
9 changed files with 389 additions and 45 deletions

View File

@ -65,13 +65,17 @@ test {
exclude '**/*IntegrationTest*'
}
task debugIntegrationTest(type: Test){
include '**/*IntegrationTest*'
debug = true
}
task integrationTest(type: Test) {
include '**/*IntegrationTest*'
}
gretty {
httpPort = 8082
httpPort = 8081
contextPath = '/'
integrationTestTask = 'integrationTest'
loggingLevel = 'TRACE'

View File

@ -23,7 +23,7 @@ public class HelloProtoBuf {
Crypto.Signature.Builder sig = Crypto.Signature.newBuilder();
sig.setData(ByteString.copyFromUtf8("deadbeef"));
msg.setSig(sig);
msg.addSig(sig);
return msg.build();
}

View File

@ -14,6 +14,7 @@ import java.sql.Statement;
import meerkat.bulletinboard.BulletinBoardServer;
import meerkat.comm.CommunicationException;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.Signature;
import meerkat.protobuf.Crypto.SignatureVerificationKey;
import meerkat.crypto.Digest;
import meerkat.crypto.concrete.SHA256Digest;
@ -57,11 +58,16 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
}
/**
* This procedure makes sure that all tags in the given list have an entry in the tags list.
* @param tagIterator
* This procedure makes sure that all tags in the given list have an entry in the tags table.
* @param tags
*/
protected abstract void insertNewTags(String[] tags) throws SQLException;
/**
* This procedure is used to convert a boolean to a BoolMsg.
* @param b is the boolean to convert.
* @return a ProtoBuf message with boolean payload.
*/
private BoolMsg boolToBoolMsg(boolean b){
return BoolMsg.newBuilder()
.setValue(b)
@ -76,31 +82,59 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
}
PreparedStatement pstmt;
ResultSet rs;
String sql;
byte[] msgID;
long entryNum;
ProtocolStringList tagList;
String[] tags;
List<Signature> signatureList;
Signature[] signatures;
// Calculate message ID (depending only on the the unsigned message)
digest.reset();
digest.update(msg);
digest.update(msg.getMsg());
msgID = digest.digest();
// Add message to table if needed and store entry number of message.
try {
sql = "INSERT INTO MsgTable (MsgId, Msg, SignerId) VALUES(?,?,?)";
pstmt = connection.prepareStatement(sql);
pstmt.setBytes(1, msgID);
pstmt.setBytes(2, msg.toByteArray());
pstmt.setBytes(3, msg.getSig().getSignerId().toByteArray());
pstmt.executeUpdate();
sql = "SELECT EntryNum From MsgTable WHERE MsgId = ?";
pstmt = connection.prepareStatement(sql);
pstmt.setBytes(1, msgID);
rs = pstmt.executeQuery();
if (rs.next()){
entryNum = rs.getLong(1);
} else{
sql = "INSERT INTO MsgTable (MsgId, Msg) VALUES(?,?)";
pstmt = connection.prepareStatement(sql);
pstmt.setBytes(1, msgID);
pstmt.setBytes(2, msg.toByteArray());
pstmt.executeUpdate();
rs = pstmt.getGeneratedKeys();
rs.next();
entryNum = rs.getLong(1);
}
pstmt.close();
} catch (SQLException e) {
throw new CommunicationException("Error inserting into MsgTable: " + e.getMessage());
}
// Retrieve tags and store new ones in tag table.
try {
tagList = msg.getMsg().getTagsList();
@ -113,12 +147,15 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
throw new CommunicationException(e.getMessage());
}
// Connect message to tags.
try{
sql = "INSERT INTO MsgTagTable (TagId, MsgId) SELECT TagTable.TagId, ? AS MsgId FROM TagTable WHERE Tag = ?";
sql = "INSERT OR IGNORE INTO MsgTagTable (TagId, EntryNum) SELECT TagTable.TagId, ? AS EntryNum FROM TagTable WHERE Tag = ?";
pstmt = connection.prepareStatement(sql);
pstmt.setLong(1, entryNum);
for (String tag : tags){
pstmt.setBytes(1, msgID);
pstmt.setString(2, tag);
pstmt.addBatch();
}
@ -129,15 +166,37 @@ public abstract class BulletinBoardSQLServer implements BulletinBoardServer{
} catch (SQLException e) {
throw new CommunicationException("Error Linking tags: " + e.getMessage());
}
// Retrieve signatures.
signatureList = msg.getSigList();
signatures = new Signature[signatureList.size()];
signatures = signatureList.toArray(signatures);
// Connect message to signatures.
try{
sql = "INSERT OR IGNORE INTO SignatureTable (EntryNum, SignerId, Signature) VALUES (?,?,?)";
pstmt = connection.prepareStatement(sql);
pstmt.setLong(1, entryNum);
for (Signature sig : signatures){
pstmt.setBytes(2, sig.getSignerId().toByteArray());
pstmt.setBytes(3, sig.toByteArray());
pstmt.addBatch();
}
pstmt.executeBatch();
pstmt.close();
} catch (SQLException e) {
throw new CommunicationException("Error Linking tags: " + e.getMessage());
}
return boolToBoolMsg(true);
}
@Override
public BulletinBoardMessageList readMessages(MessageFilterList filterList) {
// TODO Auto-generated method stub
return null;
}
public String testPrint(){

View File

@ -2,8 +2,10 @@ package meerkat.bulletinboard.sqlserver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
@ -14,7 +16,10 @@ import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import com.google.protobuf.InvalidProtocolBufferException;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.protobuf.Crypto.Signature;
import meerkat.rest.Constants;
import meerkat.bulletinboard.sqlserver.BulletinBoardSQLServer;
import meerkat.comm.CommunicationException;
@ -39,10 +44,14 @@ public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
connection = DriverManager.getConnection("jdbc:sqlite:local-instances/meerkat.db");
Statement statement = connection.createStatement();
statement.setQueryTimeout(TIMEOUT);
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB, SignerId BLOB)");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTable (EntryNum INTEGER PRIMARY KEY, MsgId BLOB UNIQUE, Msg BLOB)");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS TagTable (TagId INTEGER PRIMARY KEY, Tag varchar(50) UNIQUE)");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTagTable (MsgId BLOB, TagId INTEGER, FOREIGN KEY (MsgId) REFERENCES MsgTable(MsgId), FOREIGN KEY (TagId) REFERENCES TagTable(TagId))");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS MsgTagTable (EntryNum BLOB, TagId INTEGER, FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum), FOREIGN KEY (TagId) REFERENCES TagTable(TagId), UNIQUE (EntryNum, TagID))");
statement.executeUpdate("CREATE TABLE IF NOT EXISTS SignatureTable (EntryNum BLOB, SignerId BLOB, Signature BLOB UNIQUE, FOREIGN KEY (EntryNum) REFERENCES MsgTable(EntryNum))");
statement.executeUpdate("CREATE INDEX IF NOT EXISTS SignerIndex ON SignatureTable(SignerId)");
statement.close();
@ -107,8 +116,167 @@ public class SQLiteBulletinBoardServer extends BulletinBoardSQLServer {
@Consumes(Constants.MEDIATYPE_PROTOBUF)
@Produces(Constants.MEDIATYPE_PROTOBUF)
@Override
public BulletinBoardMessageList readMessages(MessageFilterList filterList) {
return super.readMessages(filterList);
public BulletinBoardMessageList readMessages(MessageFilterList filterList) throws CommunicationException{
PreparedStatement pstmt;
ResultSet messages, signatures;
long entryNum;
BulletinBoardMessageList.Builder resultListBuilder = BulletinBoardMessageList.newBuilder();
BulletinBoardMessage.Builder messageBuilder;
String sql;
String sqlSuffix = "";
List<MessageFilter> filters = filterList.getFilterList();
int i;
boolean tagsRequired = false;
boolean signaturesRequired = false;
boolean isFirstFilter = true;
// Check if Tag/Signature tables are required for filtering purposes.
for (MessageFilter filter : filters){
if (filter.getType() == FilterType.TAG){
tagsRequired = true;
} else if (filter.getType() == FilterType.SIGNER_ID){
signaturesRequired = true;
}
}
sql = "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable";
if (tagsRequired){
sql += " INNER JOIN MsgTagTable ON MsgTable.EntryNum = MsgTagTable.EntryNum";
sql += " INNER JOIN TagTable ON TagTable.TagId = MsgTagTable.TagId";
}
if (signaturesRequired){
sql += " INNER JOIN SignatureTable ON SignatureTable.EntryNum = MsgTable.EntryNum";
}
// Add conditions.
if (!filters.isEmpty()){
sql += " WHERE";
for (MessageFilter filter : filters){
if (filter.getType().getNumber() != FilterType.MAX_MESSAGES_VALUE){
if (isFirstFilter){
isFirstFilter = false;
} else{
sql += " AND";
}
}
switch (filter.getType().getNumber()){
case FilterType.EXACT_ENTRY_VALUE:
sql += " MsgTable.EntryNum = ?";
break;
case FilterType.MAX_ENTRY_VALUE:
sql += " MsgTable.EntryNum <= ?";
break;
case FilterType.MAX_MESSAGES_VALUE:
sqlSuffix += " LIMIT = ?";
break;
case FilterType.MSG_ID_VALUE:
sql += " MsgTableMsgId = ?";
break;
case FilterType.SIGNER_ID_VALUE:
sql += " SignatureTable.SignerId = ?";
break;
case FilterType.TAG_VALUE:
sql += " TagTable.Tag = ?";
break;
}
}
sql += sqlSuffix;
}
// Make query.
try {
pstmt = connection.prepareStatement(sql);
// Specify values for filters.
i = 1;
for (MessageFilter filter : filters){
switch (filter.getType().getNumber()){
case FilterType.EXACT_ENTRY_VALUE: // Go through.
case FilterType.MAX_ENTRY_VALUE:
pstmt.setLong(i, filter.getEntry());
i++;
break;
case FilterType.MSG_ID_VALUE: // Go through.
case FilterType.SIGNER_ID_VALUE:
pstmt.setBytes(i, filter.getId().toByteArray());
i++;
break;
case FilterType.TAG_VALUE:
pstmt.setString(i, filter.getTag());
break;
// The max-messages condition is applied as a suffix. Therefore, it is treated differently.
case FilterType.MAX_MESSAGES_VALUE:
pstmt.setLong(filters.size(), filter.getMaxMessages());
break;
}
}
// Run query.
messages = pstmt.executeQuery();
// Compile list of messages.
sql = "SELECT Signature FROM SignatureTable WHERE EntryNum = ?";
pstmt = connection.prepareStatement(sql);
while (messages.next()){
// Get entry number and retrieve signatures.
entryNum = messages.getLong(1);
pstmt.setLong(1, entryNum);
signatures = pstmt.executeQuery();
// Create message and append signatures.
messageBuilder = BulletinBoardMessage.newBuilder()
.setEntryNum(entryNum)
.setMsg(UnsignedBulletinBoardMessage.parseFrom(messages.getBytes(2)));
while (signatures.next()){
messageBuilder.addSig(Signature.parseFrom(signatures.getBytes(1)));
}
// Finalize message and add to message list.
resultListBuilder.addMessage(messageBuilder.build());
}
pstmt.close();
} catch (SQLException e){
throw new CommunicationException("Error reading messages from DB: " + e.getMessage());
} catch (InvalidProtocolBufferException e) {
throw new CommunicationException("Invalid data from DB: " + e.getMessage());
}
//Combine results and return.
return resultListBuilder.build();
}
@GET

View File

@ -38,7 +38,7 @@ public class HelloProtoWebApp {
.addTags("Signature")
.addTags("Trustee")
.setData(ByteString.copyFrom(b1)).build())
.setSig(Signature.newBuilder()
.addSig(Signature.newBuilder()
.setType(SignatureType.DSA)
.setData(ByteString.copyFrom(b2))
.setSignerId(ByteString.copyFrom(b3)).build())

View File

@ -2,6 +2,8 @@ package meerkat.bulletinboard;
import com.google.protobuf.ByteString;
import com.google.protobuf.TextFormat;
import meerkat.protobuf.Crypto.*;
import meerkat.protobuf.BulletinBoardAPI.*;
import meerkat.rest.Constants;
@ -20,17 +22,20 @@ import javax.ws.rs.core.Response;
public class SQLiteServerIntegrationTest {
private static String PROP_GETTY_URL = "gretty.httpBaseURI";
private static String DEFAULT_BASE_URL = "localhost:8082";
private static String DEFAULT_BASE_URL = "localhost:8081";
private static String BASE_URL = System.getProperty(PROP_GETTY_URL, DEFAULT_BASE_URL);
private static String SQL_SERVER_URL = "sqlserver/postmessage";
private static String SQL_SERVER_POST = "sqlserver/postmessage";
private static String SQL_SERVER_GET = "sqlserver/readmessages";
Client client;
// Connection connection;
@Before
public void setup() throws Exception {
client = ClientBuilder.newClient();
client.register(ProtobufMessageBodyReader.class);
client.register(ProtobufMessageBodyWriter.class);
}
@Test
@ -38,31 +43,131 @@ public class SQLiteServerIntegrationTest {
byte[] b1 = {(byte) 1, (byte) 2, (byte) 3, (byte) 4};
byte[] b2 = {(byte) 11, (byte) 12, (byte) 13, (byte) 14};
byte[] b3 = {(byte) 21, (byte) 22, (byte) 23, (byte) 24};
byte[] b4 = {(byte) 4, (byte) 5, (byte) 100, (byte) -50, (byte) 0};
WebTarget webTarget;
Response response;
BoolMsg bool;
BulletinBoardMessage msg;
MessageFilterList filterList;
BulletinBoardMessageList msgList;
// try{
// connection = DriverManager.getConnection("jdbc:sqlite:d:/arbel/projects/meerkat-java/bulletin-board-server/local-instances/meerkat.db");
// } catch (SQLException e) {
// System.err.println(e.getMessage());
// assert false;
// }
// Test writing mechanism
System.err.println("******** Testing: " + SQL_SERVER_POST);
webTarget = client.target(BASE_URL).path(SQL_SERVER_POST);
msg = BulletinBoardMessage.newBuilder()
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
.addTags("Signature")
.addTags("Trustee")
.setData(ByteString.copyFrom(b1))
.build())
.setSig(Signature.newBuilder()
.addSig(Signature.newBuilder()
.setType(SignatureType.DSA)
.setData(ByteString.copyFrom(b2))
.setSignerId(ByteString.copyFrom(b3))
.build())
.addSig(Signature.newBuilder()
.setType(SignatureType.ECDSA)
.setData(ByteString.copyFrom(b3))
.setSignerId(ByteString.copyFrom(b2))
.build())
.build();
System.err.println("******** Testing: " + SQL_SERVER_URL);
WebTarget webTarget = client.target(BASE_URL).path(SQL_SERVER_URL);
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
System.err.println(response);
BoolMsg bool = response.readEntity(BoolMsg.class);
bool = response.readEntity(BoolMsg.class);
assert bool.getValue();
msg = BulletinBoardMessage.newBuilder()
.setMsg(UnsignedBulletinBoardMessage.newBuilder()
.addTags("Vote")
.addTags("Trustee")
.setData(ByteString.copyFrom(b4))
.build())
.addSig(Signature.newBuilder()
.setType(SignatureType.ECDSA)
.setData(ByteString.copyFrom(b4))
.setSignerId(ByteString.copyFrom(b2))
.build())
.build();
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(msg, Constants.MEDIATYPE_PROTOBUF));
System.err.println(response);
bool = response.readEntity(BoolMsg.class);
assert bool.getValue();
// Test reading mechanism
System.err.println("******** Testing: " + SQL_SERVER_GET);
webTarget = client.target(BASE_URL).path(SQL_SERVER_GET);
filterList = MessageFilterList.newBuilder()
.addFilter(
MessageFilter.newBuilder()
.setType(FilterType.TAG)
.setTag("Vote")
.build()
)
.build();
// String sql = "SELECT MsgTable.EntryNum, MsgTable.Msg FROM MsgTable INNER JOIN SignatureTable ON SignatureTable.EntryNum = MsgTable.EntryNum WHERE SignatureTable.SignerId = ?";
// PreparedStatement pstmt = connection.prepareStatement(sql);
// int i=1;
// for (MessageFilter filter : filterList.getFilterList()){
//
// switch (filter.getType().getNumber()){
//
// case FilterType.EXACT_ENTRY_VALUE: // Go through.
// case FilterType.MAX_ENTRY_VALUE:
// pstmt.setLong(i, filter.getEntry());
// i++;
// break;
//
// case FilterType.MSG_ID_VALUE: // Go through.
// case FilterType.SIGNER_ID_VALUE:
// pstmt.setBytes(i, filter.getId().toByteArray());
// i++;
// break;
//
// case FilterType.TAG_VALUE:
// pstmt.setString(i, filter.getTag());
// break;
//
// // The max-messages condition is applied as a suffix. Therefore, it is treated differently.
// case FilterType.MAX_MESSAGES_VALUE:
// pstmt.setLong(filterList.getFilterList().size(), filter.getMaxMessages());
// break;
//
// }
// }
// ResultSet rs = pstmt.executeQuery();
//
// i = 0;
// while (rs.next()){
// i++;
// assert rs.getBytes(2)
// }
// System.err.println("Local DB size = " + i);
// pstmt.close();
response = webTarget.request(Constants.MEDIATYPE_PROTOBUF).post(Entity.entity(filterList, Constants.MEDIATYPE_PROTOBUF));
System.err.println(response);
msgList = response.readEntity(BulletinBoardMessageList.class);
System.err.println("List size: " + msgList.getMessageCount());
System.err.println("This is the list:");
System.err.println(TextFormat.printToString(msgList));
assert msgList.getMessageCount() == 1;
}
}

View File

@ -32,7 +32,7 @@ public interface BulletinBoardServer{
* @param filter return only messages that match the filter (empty list means no filtering).
* @return
*/
BulletinBoardMessageList readMessages(MessageFilterList filterList);
BulletinBoardMessageList readMessages(MessageFilterList filterList) throws CommunicationException;
/**
* This method closes the connection to the DB.

View File

@ -26,24 +26,28 @@ message UnsignedBulletinBoardMessage {
}
message BulletinBoardMessage {
// Serial entry number of message in database
int64 entryNum = 1;
// Unsigned raw data of message
UnsignedBulletinBoardMessage msg = 2;
UnsignedBulletinBoardMessage msg = 1;
// Signature of message (and tags)
meerkat.Signature sig = 2;
// Signature of message (and tags), excluding the entry number.
repeated meerkat.Signature sig = 3;
}
message BulletinBoardMessageList {
repeated BulletinBoardMessage messages = 1;
repeated BulletinBoardMessage message = 1;
}
enum FilterType {
ID = 0; // Match exact message ID
MSG_ID = 0; // Match exact message ID
EXACT_ENTRY = 1; // Match exact entry number in database (chronological)
MAX_ENTRY = 2; // Find all entries in database up to specified entry number (chronological)
SIGNATURE = 3; // Find all entries in database that correspond to specific signature (signer)
SIGNER_ID = 3; // Find all entries in database that correspond to specific signature (signer)
TAG = 4; // Find all entries in database that have a specific tag
MAX_MESSAGES = 5; // Return at most some specified number of messages
}
@ -52,14 +56,18 @@ message MessageFilter {
FilterType type = 1;
// Concrete data input to filter
bytes filter = 2;
oneof filter{
bytes id = 2;
int64 entry = 3;
string tag = 4;
int64 maxMessages = 5;
}
}
message MessageFilterList {
// Combination of filters.
// To be implemented using intersection ("AND") operations.
repeated MessageFilter filters = 1;
repeated MessageFilter filter = 1;
}

View File

@ -2,7 +2,7 @@ package meerkat.crypto.concrete;
import com.google.protobuf.ByteString;
import meerkat.protobuf.Crypto;
import meerkat.protobuf.Bulletinboardserver.*;
import meerkat.protobuf.BulletinBoardAPI.*;
import org.junit.Test;
import java.io.ByteArrayInputStream;