Introduce UUIDMap for storing objects that are keyed by a UUID

This patch introduces the new ``UUIDMap`` type, reducing code duplication and
making UUID lookups faster. We currently already use UUIDs as the identifier for
the ``DatabaseEntry`` and ``Slot`` types, but the way lookups by UUID work are
kind of ugly, as we simply iterate over the list until we find a match. As we're
probably going to have more types like this soon (groups and icons, for
example), I figured it'd be good to abstract this away into a separate type and
make it a map instead of a list.

The only thing that has gotten slower is the ``swap`` method. The internal
``LinkedHashMap`` retains insertion order with a linked list, but does not know
about the position of the values, so we basically have to copy the entire map to
simply swap two values. I don't think it's too big of a deal, because swap
operations still take less than a millisecond even with large vaults, but
suggestions for improving this are welcome.

I had to update gradle and JUnit to be able to use the new ``assertThrows``
assertion method, so this patch includes that as well.
This commit is contained in:
Alexander Bakker 2019-06-10 18:25:44 +02:00
parent 6769fefd00
commit 2323d89938
21 changed files with 375 additions and 243 deletions

View file

@ -3,12 +3,12 @@ package com.beemdevelopment.aegis;
import com.beemdevelopment.aegis.crypto.otp.HOTP;
import com.beemdevelopment.aegis.crypto.otp.OTP;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
public class HOTPTest {
// https://tools.ietf.org/html/rfc4226#page-32

View file

@ -5,13 +5,13 @@ import com.beemdevelopment.aegis.crypto.SCryptParameters;
import com.beemdevelopment.aegis.encoding.Hex;
import com.beemdevelopment.aegis.encoding.HexException;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import javax.crypto.SecretKey;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
public class SCryptTest {
@Test

View file

@ -4,12 +4,12 @@ import com.beemdevelopment.aegis.crypto.otp.OTP;
import com.beemdevelopment.aegis.crypto.otp.TOTP;
import com.beemdevelopment.aegis.encoding.HexException;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import static org.junit.Assert.*;
import static org.junit.jupiter.api.Assertions.*;
public class TOTPTest {
private static class Vector {

View file

@ -0,0 +1,104 @@
package com.beemdevelopment.aegis;
import com.beemdevelopment.aegis.util.Cloner;
import com.beemdevelopment.aegis.util.UUIDMap;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.BeforeEach;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class UUIDMapTest {
private UUIDMap<Value> _map;
@BeforeEach
public void init() {
_map = new UUIDMap<>();
}
@Test
public void addValue() {
// try adding a new value
Value value = addNewValue();
// try re-adding the value
assertThrows(AssertionError.class, () -> _map.add(value));
// try adding a clone of the value
assertThrows(AssertionError.class, () -> _map.add(Cloner.clone(value)));
}
@Test
public void removeValue() {
// try removing a value
final Value value = addNewValue();
Value oldValue = _map.remove(value);
assertFalse(_map.has(value));
// ensure we got the original value back
assertEquals(value, oldValue);
// try removing a non-existent value
assertThrows(AssertionError.class, () -> _map.remove(value));
// try removing a value using a clone
Value value2 = addNewValue();
_map.remove(Cloner.clone(value2));
assertFalse(_map.has(value2));
}
@Test
public void replaceValue() {
Value value = addNewValue();
// replace the value with a clone
Value valueClone = Cloner.clone(value);
Value oldValue = _map.replace(valueClone);
// ensure we got the original value back
assertEquals(value, oldValue);
// ensure that the clone is now stored in the map
assertSame(_map.getByUUID(value.getUUID()), valueClone);
}
@Test
public void swapValue() {
Collection<Value> values = _map.getValues();
// set up the map with some values
Value value1 = addNewValue();
Value value2 = addNewValue();
Value value3 = addNewValue();
Value value4 = addNewValue();
// set up a reference list with the reverse order
List<Value> ref = new ArrayList<>(values);
Collections.reverse(ref);
// the lists should not be equal at this point
assertNotEquals(values, ref);
// swap the values and see if the lists are equal now
_map.swap(value1, value4);
_map.swap(value2, value3);
assertIterableEquals(values, ref);
}
private Value addNewValue() {
Value value = new Value();
assertFalse(_map.has(value));
_map.add(value);
assertTrue(_map.has(value));
return value;
}
private static class Value extends UUIDMap.Value {
}
}