Merge branch '382-message-dependencies' into 'master'

Introduce a MessageContext class to be used by all validators

This change will allow to pass message dependencies from the client validators to the `ValidationManager`.

Please see my thoughts in #382 for more details.

See merge request !197
This commit is contained in:
str4d
2016-05-20 02:49:04 +00:00
18 changed files with 209 additions and 72 deletions

View File

@@ -4,13 +4,16 @@ import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager.QueueMessageValidator;
import org.briarproject.api.clients.QueueMessage;
import org.briarproject.api.clients.BdfMessageContext;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.InvalidMessageException;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.ValidationManager.MessageValidator;
import org.briarproject.api.sync.MessageContext;
import org.briarproject.api.system.Clock;
import org.briarproject.util.StringUtils;
@@ -37,43 +40,41 @@ public abstract class BdfMessageValidator implements MessageValidator,
this.clock = clock;
}
protected abstract BdfDictionary validateMessage(Message m, Group g,
BdfList body) throws FormatException;
protected abstract BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws InvalidMessageException, FormatException;
@Override
public Metadata validateMessage(Message m, Group g) {
public MessageContext validateMessage(Message m, Group g)
throws InvalidMessageException {
return validateMessage(m, g, MESSAGE_HEADER_LENGTH);
}
@Override
public Metadata validateMessage(QueueMessage q, Group g) {
public MessageContext validateMessage(QueueMessage q, Group g)
throws InvalidMessageException {
return validateMessage(q, g, QUEUE_MESSAGE_HEADER_LENGTH);
}
private Metadata validateMessage(Message m, Group g, int headerLength) {
private MessageContext validateMessage(Message m, Group g, int headerLength)
throws InvalidMessageException {
// Reject the message if it's too far in the future
long now = clock.currentTimeMillis();
if (m.getTimestamp() - now > MAX_CLOCK_DIFFERENCE) {
LOG.info("Timestamp is too far in the future");
return null;
throw new InvalidMessageException(
"Timestamp is too far in the future");
}
byte[] raw = m.getRaw();
if (raw.length <= headerLength) {
LOG.info("Message is too short");
return null;
throw new InvalidMessageException("Message is too short");
}
try {
BdfList body = clientHelper.toList(raw, headerLength,
raw.length - headerLength);
BdfDictionary meta = validateMessage(m, g, body);
if (meta == null) {
LOG.info("Invalid message");
return null;
}
return metadataEncoder.encode(meta);
BdfMessageContext result = validateMessage(m, g, body);
Metadata meta = metadataEncoder.encode(result.getDictionary());
return new MessageContext(meta, result.getDependencies());
} catch (FormatException e) {
LOG.info("Invalid message");
return null;
throw new InvalidMessageException(e);
}
}

View File

@@ -14,10 +14,12 @@ import org.briarproject.api.db.Transaction;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.InvalidMessageException;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.api.sync.MessageContext;
import org.briarproject.util.ByteUtils;
import java.util.ArrayList;
@@ -169,7 +171,8 @@ class MessageQueueManagerImpl implements MessageQueueManager {
}
@Override
public Metadata validateMessage(Message m, Group g) {
public MessageContext validateMessage(Message m, Group g)
throws InvalidMessageException {
byte[] raw = m.getRaw();
if (raw.length < QUEUE_MESSAGE_HEADER_LENGTH) return null;
long queuePosition = ByteUtils.readUint64(raw,

View File

@@ -3,6 +3,7 @@ package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.BdfMessageContext;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyParser;
import org.briarproject.api.crypto.PublicKey;
@@ -13,6 +14,7 @@ import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.InvalidMessageException;
import org.briarproject.api.sync.Message;
import org.briarproject.api.system.Clock;
import org.briarproject.clients.BdfMessageValidator;
@@ -39,8 +41,8 @@ class ForumPostValidator extends BdfMessageValidator {
}
@Override
protected BdfDictionary validateMessage(Message m, Group g,
BdfList body) throws FormatException {
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws InvalidMessageException, FormatException {
// Parent ID, author, content type, forum post body, signature
checkSize(body, 5);
// Parent ID is optional
@@ -69,12 +71,10 @@ class ForumPostValidator extends BdfMessageValidator {
checkLength(sig, 0, MAX_SIGNATURE_LENGTH);
// If there's an author there must be a signature and vice versa
if (author != null && sig == null) {
LOG.info("Author without signature");
return null;
throw new InvalidMessageException("Author without signature");
}
if (author == null && sig != null) {
LOG.info("Signature without author");
return null;
throw new InvalidMessageException("Signature without author");
}
// Verify the signature, if any
if (author != null) {
@@ -90,12 +90,10 @@ class ForumPostValidator extends BdfMessageValidator {
signature.initVerify(key);
signature.update(clientHelper.toByteArray(signed));
if (!signature.verify(sig)) {
LOG.info("Invalid signature");
return null;
throw new InvalidMessageException("Invalid signature");
}
} catch (GeneralSecurityException e) {
LOG.info("Invalid public key");
return null;
throw new InvalidMessageException("Invalid public key");
}
}
// Return the metadata
@@ -111,6 +109,6 @@ class ForumPostValidator extends BdfMessageValidator {
}
meta.put("contentType", contentType);
meta.put("read", false);
return meta;
return new BdfMessageContext(meta);
}
}

View File

@@ -3,6 +3,7 @@ package org.briarproject.forum;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.clients.BdfMessageContext;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
@@ -36,7 +37,7 @@ class ForumSharingValidator extends BdfMessageValidator {
}
@Override
protected BdfDictionary validateMessage(Message m, Group g,
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws FormatException {
BdfDictionary d = new BdfDictionary();
@@ -75,6 +76,6 @@ class ForumSharingValidator extends BdfMessageValidator {
d.put(SESSION_ID, id);
d.put(LOCAL, false);
d.put(TIME, m.getTimestamp());
return d;
return new BdfMessageContext(d);
}
}

View File

@@ -2,10 +2,11 @@ package org.briarproject.introduction;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.clients.BdfMessageContext;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
import org.briarproject.api.clients.SessionId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.Message;
import org.briarproject.api.system.Clock;
@@ -42,7 +43,7 @@ class IntroductionValidator extends BdfMessageValidator {
}
@Override
protected BdfDictionary validateMessage(Message m, Group g, BdfList body)
protected BdfMessageContext validateMessage(Message m, Group g, BdfList body)
throws FormatException {
BdfDictionary d;
@@ -67,7 +68,7 @@ class IntroductionValidator extends BdfMessageValidator {
d.put(GROUP_ID, m.getGroupId());
d.put(MESSAGE_ID, m.getId());
d.put(MESSAGE_TIME, m.getTimestamp());
return d;
return new BdfMessageContext(d);
}
private BdfDictionary validateRequest(BdfList message)

View File

@@ -3,6 +3,7 @@ package org.briarproject.messaging;
import org.briarproject.api.FormatException;
import org.briarproject.api.UniqueId;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.BdfMessageContext;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
@@ -22,7 +23,7 @@ class PrivateMessageValidator extends BdfMessageValidator {
}
@Override
protected BdfDictionary validateMessage(Message m, Group g,
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws FormatException {
// Parent ID, content type, private message body
checkSize(body, 3);
@@ -42,6 +43,6 @@ class PrivateMessageValidator extends BdfMessageValidator {
meta.put("contentType", contentType);
meta.put("local", false);
meta.put("read", false);
return meta;
return new BdfMessageContext(meta);
}
}

View File

@@ -2,6 +2,7 @@ package org.briarproject.properties;
import org.briarproject.api.FormatException;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.BdfMessageContext;
import org.briarproject.api.data.BdfDictionary;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.MetadataEncoder;
@@ -22,7 +23,7 @@ public class TransportPropertyValidator extends BdfMessageValidator {
}
@Override
protected BdfDictionary validateMessage(Message m, Group g,
protected BdfMessageContext validateMessage(Message m, Group g,
BdfList body) throws FormatException {
// Transport ID, version, properties
checkSize(body, 3);
@@ -45,6 +46,6 @@ public class TransportPropertyValidator extends BdfMessageValidator {
meta.put("transportId", transportId);
meta.put("version", version);
meta.put("local", false);
return meta;
return new BdfMessageContext(meta);
}
}

View File

@@ -16,9 +16,11 @@ import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.InvalidMessageException;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.sync.MessageContext;
import org.briarproject.util.ByteUtils;
import java.util.LinkedList;
@@ -31,6 +33,7 @@ import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
@@ -152,31 +155,54 @@ class ValidationManagerImpl implements ValidationManager, Service,
if (v == null) {
LOG.warning("No validator");
} else {
Metadata meta = v.validateMessage(m, g);
storeValidationResult(m, g.getClientId(), meta);
try {
MessageContext context = v.validateMessage(m, g);
storeMessageContext(m, g.getClientId(), context);
} catch (InvalidMessageException e) {
if (LOG.isLoggable(INFO))
LOG.log(INFO, e.toString(), e);
markMessageInvalid(m, g.getClientId());
}
}
}
});
}
private void storeValidationResult(final Message m, final ClientId c,
final Metadata meta) {
private void storeMessageContext(final Message m, final ClientId c,
final MessageContext result) {
dbExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Transaction txn = db.startTransaction(false);
try {
if (meta == null) {
db.setMessageValid(txn, m, c, false);
} else {
db.mergeMessageMetadata(txn, m.getId(), meta);
db.setMessageValid(txn, m, c, true);
db.setMessageShared(txn, m, true);
IncomingMessageHook hook = hooks.get(c);
if (hook != null)
hook.incomingMessage(txn, m, meta);
}
Metadata meta = result.getMetadata();
db.mergeMessageMetadata(txn, m.getId(), meta);
db.setMessageValid(txn, m, c, true);
db.setMessageShared(txn, m, true);
IncomingMessageHook hook = hooks.get(c);
if (hook != null)
hook.incomingMessage(txn, m, meta);
txn.setComplete();
} finally {
db.endTransaction(txn);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void markMessageInvalid(final Message m, final ClientId c) {
dbExecutor.execute(new Runnable() {
@Override
public void run() {
try {
Transaction txn = db.startTransaction(false);
try {
db.setMessageValid(txn, m, c, false);
txn.setComplete();
} finally {
db.endTransaction(txn);