Identity manager hooks. #209

This commit is contained in:
akwizgran
2016-01-20 12:03:15 +00:00
parent 82cf12040f
commit c4692a7007
16 changed files with 258 additions and 53 deletions

View File

@@ -13,6 +13,7 @@ import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager.IdentityRemovedHook;
import org.briarproject.api.lifecycle.Service;
import java.util.ArrayList;
@@ -27,7 +28,8 @@ import static org.briarproject.api.contact.Contact.Status.ACTIVE;
import static org.briarproject.api.contact.Contact.Status.ADDING;
import static org.briarproject.api.contact.Contact.Status.REMOVING;
class ContactManagerImpl implements ContactManager, Service {
class ContactManagerImpl implements ContactManager, Service,
IdentityRemovedHook {
private static final Logger LOG =
Logger.getLogger(ContactManagerImpl.class.getName());
@@ -118,4 +120,14 @@ class ContactManagerImpl implements ContactManager, Service {
db.removeContact(c);
eventBus.broadcast(new ContactRemovedEvent(c));
}
@Override
public void identityRemoved(AuthorId a) {
// Remove any contacts of the local pseudonym that's being removed
try {
for (ContactId c : db.getContacts(a)) removeContact(c);
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
}
}

View File

@@ -4,6 +4,7 @@ import com.google.inject.AbstractModule;
import com.google.inject.Provides;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import javax.inject.Singleton;
@@ -15,8 +16,10 @@ public class ContactModule extends AbstractModule {
@Provides @Singleton
ContactManager getContactManager(LifecycleManager lifecycleManager,
IdentityManager identityManager,
ContactManagerImpl contactManager) {
lifecycleManager.register(contactManager);
identityManager.registerIdentityRemovedHook(contactManager);
return contactManager;
}
}

View File

@@ -334,7 +334,7 @@ interface Database<T> {
* Locking: read
*/
Collection<MessageStatus> getMessageStatus(T txn, ContactId c, GroupId g)
throws DbException;
throws DbException;
/**
* Returns the status of the given message with respect to the given
@@ -343,7 +343,7 @@ interface Database<T> {
* Locking: read
*/
MessageStatus getMessageStatus(T txn, ContactId c, MessageId m)
throws DbException;
throws DbException;
/**
* Returns the IDs of some messages received from the given contact that
@@ -388,7 +388,7 @@ interface Database<T> {
* Locking: read.
*/
Collection<MessageId> getMessagesToValidate(T txn, ClientId c)
throws DbException;
throws DbException;
/**
* Returns the message with the given ID, in serialised form.
@@ -639,6 +639,14 @@ interface Database<T> {
void setContactStatus(T txn, ContactId c, Contact.Status s)
throws DbException;
/**
* Sets the status of the given local pseudonym.
* <p>
* Locking: write.
*/
void setLocalAuthorStatus(T txn, AuthorId a, LocalAuthor.Status s)
throws DbException;
/**
* Marks the given message as valid or invalid.
* <p>

View File

@@ -16,10 +16,7 @@ import org.briarproject.api.db.NoSuchLocalAuthorException;
import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.db.NoSuchSubscriptionException;
import org.briarproject.api.db.NoSuchTransportException;
import org.briarproject.api.event.ContactRemovedEvent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.LocalAuthorAddedEvent;
import org.briarproject.api.event.LocalAuthorRemovedEvent;
import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
import org.briarproject.api.event.LocalTransportsUpdatedEvent;
import org.briarproject.api.event.MessageAddedEvent;
@@ -218,7 +215,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} finally {
lock.writeLock().unlock();
}
eventBus.broadcast(new LocalAuthorAddedEvent(a.getId()));
}
public void addLocalMessage(Message m, ClientId c, Metadata meta)
@@ -572,6 +568,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
}
public Collection<ContactId> getContacts(AuthorId a) throws DbException {
lock.readLock().lock();
try {
T txn = db.startTransaction();
try {
if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
Collection<ContactId> contacts = db.getContacts(txn, a);
db.commitTransaction(txn);
return contacts;
} catch (DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
lock.readLock().unlock();
}
}
public Group getGroup(GroupId g) throws DbException {
lock.readLock().lock();
try {
@@ -1242,14 +1257,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
public void removeLocalAuthor(AuthorId a) throws DbException {
Collection<ContactId> affected;
lock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
affected = db.getContacts(txn, a);
db.removeLocalAuthor(txn, a);
db.commitTransaction(txn);
} catch (DbException e) {
@@ -1259,9 +1272,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} finally {
lock.writeLock().unlock();
}
for (ContactId c : affected)
eventBus.broadcast(new ContactRemovedEvent(c));
eventBus.broadcast(new LocalAuthorRemovedEvent(a));
}
public void removeTransport(TransportId t) throws DbException {
@@ -1302,6 +1312,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
}
public void setLocalAuthorStatus(AuthorId a, LocalAuthor.Status s)
throws DbException {
lock.writeLock().lock();
try {
T txn = db.startTransaction();
try {
if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
db.setLocalAuthorStatus(txn, a, s);
db.commitTransaction(txn);
} catch (DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
lock.writeLock().unlock();
}
}
public void setMessageValidity(Message m, ClientId c, boolean valid)
throws DbException {
lock.writeLock().lock();

View File

@@ -64,8 +64,8 @@ import static org.briarproject.db.ExponentialBackoff.calculateExpiry;
*/
abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 15;
private static final int MIN_SCHEMA_VERSION = 15;
private static final int SCHEMA_VERSION = 16;
private static final int MIN_SCHEMA_VERSION = 16;
private static final String CREATE_SETTINGS =
"CREATE TABLE settings"
@@ -81,6 +81,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " publicKey BINARY NOT NULL,"
+ " privateKey BINARY NOT NULL,"
+ " created BIGINT NOT NULL,"
+ " status INT NOT NULL,"
+ " PRIMARY KEY (authorId))";
private static final String CREATE_CONTACTS =
@@ -719,15 +720,16 @@ abstract class JdbcDatabase implements Database<Connection> {
throws DbException {
PreparedStatement ps = null;
try {
String sql = "INSERT INTO localAuthors"
+ " (authorId, name, publicKey, privateKey, created)"
+ " VALUES (?, ?, ?, ?, ?)";
String sql = "INSERT INTO localAuthors (authorId, name, publicKey,"
+ " privateKey, created, status)"
+ " VALUES (?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql);
ps.setBytes(1, a.getId().getBytes());
ps.setString(2, a.getName());
ps.setBytes(3, a.getPublicKey());
ps.setBytes(4, a.getPrivateKey());
ps.setLong(5, a.getTimeCreated());
ps.setInt(6, a.getStatus().getValue());
int affected = ps.executeUpdate();
if (affected != 1) throw new DbStateException();
ps.close();
@@ -1345,7 +1347,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT name, publicKey, privateKey, created"
String sql = "SELECT name, publicKey, privateKey, created, status"
+ " FROM localAuthors"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
@@ -1356,8 +1358,10 @@ abstract class JdbcDatabase implements Database<Connection> {
byte[] publicKey = rs.getBytes(2);
byte[] privateKey = rs.getBytes(3);
long created = rs.getLong(4);
LocalAuthor.Status status = LocalAuthor.Status.fromValue(
rs.getInt(5));
LocalAuthor localAuthor = new LocalAuthor(a, name, publicKey,
privateKey, created);
privateKey, created, status);
if (rs.next()) throw new DbStateException();
rs.close();
ps.close();
@@ -1374,7 +1378,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT authorId, name, publicKey, privateKey, created"
String sql = "SELECT authorId, name, publicKey, privateKey,"
+ " created, status"
+ " FROM localAuthors";
ps = txn.prepareStatement(sql);
rs = ps.executeQuery();
@@ -1385,8 +1390,10 @@ abstract class JdbcDatabase implements Database<Connection> {
byte[] publicKey = rs.getBytes(3);
byte[] privateKey = rs.getBytes(4);
long created = rs.getLong(5);
LocalAuthor.Status status = LocalAuthor.Status.fromValue(
rs.getInt(6));
authors.add(new LocalAuthor(authorId, name, publicKey,
privateKey, created));
privateKey, created, status));
}
rs.close();
ps.close();
@@ -2398,7 +2405,8 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void mergeSettings(Connection txn, Settings s, String namespace) throws DbException {
public void mergeSettings(Connection txn, Settings s, String namespace)
throws DbException {
PreparedStatement ps = null;
try {
// Update any settings that already exist
@@ -2712,6 +2720,24 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public void setLocalAuthorStatus(Connection txn, AuthorId a,
LocalAuthor.Status s) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE localAuthors SET status = ?"
+ " WHERE authorId = ?";
ps = txn.prepareStatement(sql);
ps.setInt(1, s.getValue());
ps.setBytes(2, a.getBytes());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps);
throw new DbException(e);
}
}
public void setMessageValidity(Connection txn, MessageId m, boolean valid)
throws DbException {
PreparedStatement ps = null;

View File

@@ -4,38 +4,114 @@ import com.google.inject.Inject;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.NoSuchLocalAuthorException;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.event.LocalAuthorAddedEvent;
import org.briarproject.api.event.LocalAuthorRemovedEvent;
import org.briarproject.api.identity.AuthorId;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.Service;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Logger;
class IdentityManagerImpl implements IdentityManager {
import static java.util.logging.Level.WARNING;
import static org.briarproject.api.identity.LocalAuthor.Status.ACTIVE;
import static org.briarproject.api.identity.LocalAuthor.Status.ADDING;
import static org.briarproject.api.identity.LocalAuthor.Status.REMOVING;
class IdentityManagerImpl implements IdentityManager, Service {
private static final Logger LOG =
Logger.getLogger(IdentityManagerImpl.class.getName());
private final DatabaseComponent db;
private final EventBus eventBus;
private final List<IdentityAddedHook> addHooks;
private final List<IdentityRemovedHook> removeHooks;
@Inject
IdentityManagerImpl(DatabaseComponent db) {
IdentityManagerImpl(DatabaseComponent db, EventBus eventBus) {
this.db = db;
this.eventBus = eventBus;
addHooks = new CopyOnWriteArrayList<IdentityAddedHook>();
removeHooks = new CopyOnWriteArrayList<IdentityRemovedHook>();
}
@Override
public boolean start() {
// Finish adding/removing any partly added/removed pseudonyms
try {
for (LocalAuthor a : db.getLocalAuthors()) {
if (a.getStatus().equals(ADDING)) {
for (IdentityAddedHook hook : addHooks)
hook.identityAdded(a.getId());
db.setLocalAuthorStatus(a.getId(), ACTIVE);
eventBus.broadcast(new LocalAuthorAddedEvent(a.getId()));
} else if (a.getStatus().equals(REMOVING)) {
for (IdentityRemovedHook hook : removeHooks)
hook.identityRemoved(a.getId());
db.removeLocalAuthor(a.getId());
eventBus.broadcast(new LocalAuthorRemovedEvent(a.getId()));
}
}
return true;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return false;
}
}
@Override
public boolean stop() {
return false;
}
@Override
public void registerIdentityAddedHook(IdentityAddedHook hook) {
addHooks.add(hook);
}
@Override
public void registerIdentityRemovedHook(IdentityRemovedHook hook) {
removeHooks.add(hook);
}
@Override
public void addLocalAuthor(LocalAuthor a) throws DbException {
db.addLocalAuthor(a);
for (IdentityAddedHook hook : addHooks) hook.identityAdded(a.getId());
db.setLocalAuthorStatus(a.getId(), ACTIVE);
eventBus.broadcast(new LocalAuthorAddedEvent(a.getId()));
}
@Override
public LocalAuthor getLocalAuthor(AuthorId a) throws DbException {
return db.getLocalAuthor(a);
LocalAuthor author = db.getLocalAuthor(a);
if (author.getStatus().equals(ACTIVE)) return author;
throw new NoSuchLocalAuthorException();
}
@Override
public Collection<LocalAuthor> getLocalAuthors() throws DbException {
return db.getLocalAuthors();
Collection<LocalAuthor> authors = db.getLocalAuthors();
// Filter out any pseudonyms that are being added or removed
List<LocalAuthor> active = new ArrayList<LocalAuthor>(authors.size());
for (LocalAuthor a : authors)
if (a.getStatus().equals(ACTIVE)) active.add(a);
return Collections.unmodifiableList(active);
}
@Override
public void removeLocalAuthor(AuthorId a) throws DbException {
db.setLocalAuthorStatus(a, REMOVING);
for (IdentityRemovedHook hook : removeHooks) hook.identityRemoved(a);
db.removeLocalAuthor(a);
eventBus.broadcast(new LocalAuthorRemovedEvent(a));
}
}

View File

@@ -14,6 +14,8 @@ import java.io.IOException;
import javax.inject.Inject;
import static org.briarproject.api.identity.LocalAuthor.Status.ADDING;
class AuthorFactoryImpl implements AuthorFactory {
private final CryptoComponent crypto;
@@ -35,7 +37,7 @@ class AuthorFactoryImpl implements AuthorFactory {
public LocalAuthor createLocalAuthor(String name, byte[] publicKey,
byte[] privateKey) {
return new LocalAuthor(getId(name, publicKey), name, publicKey,
privateKey, clock.currentTimeMillis());
privateKey, clock.currentTimeMillis(), ADDING);
}
private AuthorId getId(String name, byte[] publicKey) {