From 884eb33b2ccf00c9ac0b2649fe8cf78a5ef204d3 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Thu, 12 Sep 2024 19:13:29 +0100 Subject: [PATCH 01/66] simplify to single generic parameter --- .../java/org/linguafranca/pwdb/Database.java | 34 +- .../java/org/linguafranca/pwdb/Entry.java | 10 +- .../java/org/linguafranca/pwdb/Group.java | 28 +- .../java/org/linguafranca/pwdb/Visitor.java | 24 +- .../pwdb/base/AbstractDatabase.java | 56 +- .../linguafranca/pwdb/base/AbstractEntry.java | 4 +- .../linguafranca/pwdb/base/AbstractGroup.java | 37 +- .../pwdb/protect/ProtectedDatabase.java | 2 +- dom/pom.xml | 38 - .../pwdb/kdbx/dom/DomDatabaseWrapper.java | 223 ---- .../pwdb/kdbx/dom/DomEntryWrapper.java | 226 ---- .../pwdb/kdbx/dom/DomGroupWrapper.java | 219 --- .../pwdb/kdbx/dom/DomIconWrapper.java | 55 - .../pwdb/kdbx/dom/DomPropertyValueTest.java | 57 - .../kdbx/dom/DomSerializableDatabase.java | 245 ---- .../kdbx/dom/DomBinaryPropertyV3Test.java | 57 - .../kdbx/dom/DomBinaryPropertyV4Test.java | 63 - .../dom/DomDatabaseWrapperLoaderTest.java | 41 - .../pwdb/kdbx/dom/DomDatabaseWrapperTest.java | 65 - .../pwdb/kdbx/dom/DomHelperTest.java | 42 - .../pwdb/kdbx/dom/DomRecycleBinTest.java | 14 - .../pwdb/kdbx/dom/DomSaveAndReloadTest.java | 70 - .../linguafranca/pwdb/kdbx/dom/DomV4Test.java | 34 - .../pwdb/kdbx/dom/VisitorTest.java | 95 -- .../pwdb/kdbx/jackson/JacksonDatabase.java | 2 +- .../pwdb/kdbx/jackson/JacksonEntry.java | 5 +- .../pwdb/kdbx/jackson/JacksonGroup.java | 6 +- jaxb/bindings.xml | 62 - jaxb/gradle-source-sets.txt | 18 - jaxb/pom.xml | 111 -- .../generated/java/META-INF/sun-jaxb.episode | 53 - .../pwdb/kdbx/jaxb/binding/Adapter1.java | 27 - .../pwdb/kdbx/jaxb/binding/Adapter2.java | 26 - .../pwdb/kdbx/jaxb/binding/Adapter3.java | 27 - .../pwdb/kdbx/jaxb/binding/AutoType.java | 239 ---- .../pwdb/kdbx/jaxb/binding/Binaries.java | 201 --- .../pwdb/kdbx/jaxb/binding/BinaryField.java | 164 --- .../pwdb/kdbx/jaxb/binding/CustomData.java | 80 -- .../pwdb/kdbx/jaxb/binding/CustomIcons.java | 234 ---- .../pwdb/kdbx/jaxb/binding/History.java | 71 - .../kdbx/jaxb/binding/JaxbEntryBinding.java | 423 ------ .../kdbx/jaxb/binding/JaxbGroupBinding.java | 456 ------- .../pwdb/kdbx/jaxb/binding/KeePassFile.java | 1173 ----------------- .../pwdb/kdbx/jaxb/binding/ObjectFactory.java | 214 --- .../pwdb/kdbx/jaxb/binding/Parented.java | 34 - .../pwdb/kdbx/jaxb/binding/StringField.java | 205 --- .../pwdb/kdbx/jaxb/binding/Times.java | 245 ---- .../pwdb/kdbx/jaxb/binding/ValueExtender.java | 75 -- .../pwdb/kdbx/jaxb/JaxbDatabase.java | 199 --- .../pwdb/kdbx/jaxb/JaxbEntry.java | 283 ---- .../pwdb/kdbx/jaxb/JaxbGroup.java | 225 ---- .../linguafranca/pwdb/kdbx/jaxb/JaxbIcon.java | 61 - .../kdbx/jaxb/JaxbSerializableDatabase.java | 258 ---- .../base/AbstractJaxbParentedBinding.java | 30 - .../pwdb/kdbx/jaxb/base/ValueBinding.java | 29 - .../pwdb/kdbx/jaxb/util/DateAdapter.java | 39 - .../pwdb/kdbx/jaxb/util/JaxbUtil.java | 28 - .../pwdb/kdbx/jaxb/JaxbBasicDatabaseTest.java | 36 - .../kdbx/jaxb/JaxbBinaryPropertyV3Test.java | 58 - .../kdbx/jaxb/JaxbBinaryPropertyV4Test.java | 63 - .../pwdb/kdbx/jaxb/JaxbLoaderTest.java | 40 - .../pwdb/kdbx/jaxb/JaxbPropertyValueTest.java | 57 - .../pwdb/kdbx/jaxb/JaxbRecycleBinTest.java | 12 - .../pwdb/kdbx/jaxb/JaxbSaveAndReloadTest.java | 68 - .../jaxb/JaxbSerializableDatabaseTest.java | 49 - .../linguafranca/pwdb/kdbx/jaxb/JaxbTest.java | 59 - .../pwdb/kdbx/jaxb/JaxbV4LoadTest.java | 34 - .../org/linguafranca/pwdb/kdb/KdbGroup.java | 2 +- .../linguafranca/pwdb/kdbx/dom/DomHelper.java | 281 ---- pom.xml | 3 - simple/pom.xml | 64 - .../pwdb/kdbx/simple/SimpleDatabase.java | 216 --- .../pwdb/kdbx/simple/SimpleEntry.java | 270 ---- .../pwdb/kdbx/simple/SimpleGroup.java | 244 ---- .../pwdb/kdbx/simple/SimpleIcon.java | 60 - .../simple/SimpleSerializableDatabase.java | 218 --- .../converter/Base64ByteArrayConverter.java | 40 - .../converter/EmptyStringConverter.java | 41 - .../converter/KeePassBooleanConverter.java | 37 - .../kdbx/simple/converter/TimeConverter.java | 43 - .../kdbx/simple/converter/UuidConverter.java | 43 - .../kdbx/simple/converter/ValueConverter.java | 68 - .../pwdb/kdbx/simple/model/EntryClasses.java | 212 --- .../pwdb/kdbx/simple/model/KeePassFile.java | 265 ---- .../pwdb/kdbx/simple/model/Times.java | 122 -- .../simple/SimpleBinaryPropertyV3Test.java | 67 - .../simple/SimpleBinaryPropertyV4Test.java | 64 - .../kdbx/simple/SimpleDatabaseLoadTest.java | 77 -- .../kdbx/simple/SimpleDatabaseLoaderTest.java | 37 - .../pwdb/kdbx/simple/SimpleDatabaseTest.java | 40 - .../kdbx/simple/SimplePropertyValueTest.java | 57 - .../kdbx/simple/SimpleRecycleBinTest.java | 12 - .../kdbx/simple/SimpleSaveAndReloadTest.java | 112 -- 93 files changed, 105 insertions(+), 10108 deletions(-) delete mode 100644 dom/pom.xml delete mode 100644 dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapper.java delete mode 100644 dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomEntryWrapper.java delete mode 100644 dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomGroupWrapper.java delete mode 100644 dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomIconWrapper.java delete mode 100644 dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomPropertyValueTest.java delete mode 100644 dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomSerializableDatabase.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV3Test.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV4Test.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperLoaderTest.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperTest.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomHelperTest.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomRecycleBinTest.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomSaveAndReloadTest.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomV4Test.java delete mode 100644 dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/VisitorTest.java delete mode 100644 jaxb/bindings.xml delete mode 100644 jaxb/gradle-source-sets.txt delete mode 100644 jaxb/pom.xml delete mode 100644 jaxb/src/generated/java/META-INF/sun-jaxb.episode delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter1.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter2.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter3.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/AutoType.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Binaries.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/BinaryField.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomData.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomIcons.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/History.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbEntryBinding.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbGroupBinding.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/KeePassFile.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ObjectFactory.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Parented.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/StringField.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Times.java delete mode 100644 jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ValueExtender.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbDatabase.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbEntry.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbGroup.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbIcon.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabase.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/AbstractJaxbParentedBinding.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/ValueBinding.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/DateAdapter.java delete mode 100644 jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/JaxbUtil.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBasicDatabaseTest.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV3Test.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV4Test.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbLoaderTest.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbPropertyValueTest.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbRecycleBinTest.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSaveAndReloadTest.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabaseTest.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbTest.java delete mode 100644 jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbV4LoadTest.java delete mode 100644 kdbx/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomHelper.java delete mode 100644 simple/pom.xml delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabase.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleEntry.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleGroup.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleIcon.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleSerializableDatabase.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/Base64ByteArrayConverter.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/EmptyStringConverter.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/KeePassBooleanConverter.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/TimeConverter.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/UuidConverter.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/ValueConverter.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/EntryClasses.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/KeePassFile.java delete mode 100644 simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/Times.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV3Test.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV4Test.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoadTest.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoaderTest.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseTest.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimplePropertyValueTest.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleRecycleBinTest.java delete mode 100644 simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleSaveAndReloadTest.java diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index 632f35ae..855f92e8 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -49,26 +49,26 @@ * the caller without affecting the underlying database structure, however changes * to the Groups and Entries contained in the lists do modify the database. */ -public interface Database , G extends Group, E extends Entry, I extends Icon> { +public interface Database > { /** * get the root group for the database * @return the root group */ - G getRootGroup(); + Group getRootGroup(); /** * Create a new Group * @return the group created */ - G newGroup(); + Group newGroup(); /** * Create a new named Group * @param name the name of the group * @return the group created */ - G newGroup(String name); + Group newGroup(String name); /** * Create a new Group copying the details of the supplied group, but not copying its children @@ -77,19 +77,19 @@ public interface Database , G extends Group group); + Group newGroup(Group> group); /** * Create a new Entry * @return the entry created */ - E newEntry(); + Entry newEntry(); /** * Create a new Entry with a title * @return the entry created */ - E newEntry(String title); + Entry newEntry(String title); /** * Create a new Entry copying the details of the supplied entry @@ -98,27 +98,27 @@ public interface Database , G extends Group entry); + Entry newEntry(Entry entry); /** * Create a new default icon * @return the created icon */ - I newIcon(); + Icon newIcon(); /** * Create a new icon with a specified index * @param i the index of the icon to create * @return the created icon */ - I newIcon(Integer i); + Icon newIcon(Integer i); /** * Find an entry with this UUID anywhere in the database except the recycle bin * @param uuid the UUID * @return an entry or null if not found */ - @Nullable E findEntry(UUID uuid); + @Nullable Entry findEntry(UUID uuid); /** * Delete an entry with this UUID from anywhere in the database except the recycle bin @@ -133,7 +133,7 @@ public interface Database , G extends Group findGroup(UUID uuid); /** * Delete a group with this UUID from anywhere in the database except the recycle bin @@ -163,7 +163,7 @@ public interface Database , G extends Group getRecycleBin(); /** * empty the recycle bin whether it is enabled or disabled @@ -177,14 +177,14 @@ public interface Database , G extends Group visitor); + void visit(Visitor visitor); /** * Visit all entries starting from a group * @param group the group to start at * @param visitor the visitor to use */ - void visit(G group, Visitor visitor); + void visit(Group group, Visitor visitor); /** * Find all entries that match the criteria @@ -192,7 +192,7 @@ public interface Database , G extends Group findEntries(Entry.Matcher matcher); + List> findEntries(Entry.Matcher matcher); /** * Find all entries that match {@link Entry#match(String)} @@ -200,7 +200,7 @@ public interface Database , G extends Group findEntries(String find); + List> findEntries(String find); /** * Gets the name of the database or null if not supported diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 1818eb28..48f2d47f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -43,7 +43,7 @@ * * @author Jo */ -public interface Entry , G extends Group, E extends Entry, I extends Icon> { +public interface Entry > { /** * Standard properties are attributes of Entries that are accessible either by * dedicated methods, such as getPassword, or by {@link #getProperty(String)} @@ -67,7 +67,7 @@ public interface Entry , G extends Group> entry); } /** @@ -213,7 +213,7 @@ interface Matcher { * Get the parent of this entry * @return a parent or null if this entry currently does not have a parent */ - @Nullable G getParent(); + @Nullable Group getParent(); /** * Get the UUID of this entry. Databases (like KDB) that do not natively support @@ -342,13 +342,13 @@ interface Matcher { * Returns the {@link Icon} associated with this entry. * @return an Icon */ - I getIcon(); + Icon getIcon(); /** * Sets the {@link Icon} associated with this entry. * @param icon an Icon */ - void setIcon(I icon); + void setIcon(Icon icon); /** * Returns the date at which any value was retrieved from this entry. diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index 152a3f12..faf68395 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -37,7 +37,7 @@ * * @author Jo */ -public interface Group , G extends Group, E extends Entry, I extends Icon> { +public interface Group > { /** * Returns true if this is the root group of a database */ @@ -54,7 +54,7 @@ public interface Group , G extends Group getParent(); /** * Add this group to a parent. The group must be of a type compatible with the database @@ -62,7 +62,7 @@ public interface Group , G extends Group parent); /** * Returns a list of groups that are the children of this group. @@ -71,7 +71,7 @@ public interface Group , G extends Group getGroups(); + List> getGroups(); /** * Returns the number of groups that are direct children of this group @@ -90,7 +90,7 @@ public interface Group , G extends Group addGroup(Group group); /** * Returns a list of child Groups whose name exactly matches that supplied. @@ -100,7 +100,7 @@ public interface Group , G extends Group findGroups(String groupName); + List> findGroups(String groupName); /** * Removes the group supplied from this group. The group removed @@ -114,12 +114,12 @@ public interface Group , G extends Group removeGroup(Group group); /** * Returns a modifiable by the caller list of entries contained in this group. */ - List getEntries(); + List> getEntries(); /** * Returns the number of entries in this group @@ -142,7 +142,7 @@ public interface Group , G extends Group findEntries(String match, boolean recursive); + List> findEntries(String match, boolean recursive); /** * Finds all entries in this group that match using the matcher supplied. @@ -157,7 +157,7 @@ public interface Group , G extends Group findEntries(Entry.Matcher matcher, boolean recursive); + List> findEntries(Entry.Matcher matcher, boolean recursive); /** * Adds an entry to this group removing it from another group @@ -165,7 +165,7 @@ public interface Group , G extends Group addEntry(Entry entry); /** * Remove an entry from this group and hence from the database. @@ -173,13 +173,13 @@ public interface Group , G extends Group removeEntry( Entry entry); /** * Make a deep copy of the children a group and add to this group. Does not copy the parent group. * @param parent the group to deep copy */ - void copy(Group parent); + void copy(Group parent); /** * Returns an XPath-like string of the names of Groups from the Root @@ -210,7 +210,7 @@ public interface Group , G extends Group, G extends Group, E extends Entry, I extends Icon> { +public interface Visitor > { /** * Called on entry to a group visit * @param group the group being visited */ - void startVisit(G group); + void startVisit(Group group); /** * Called on exit from a group visit * @param group the group being exited */ - void endVisit(G group); + void endVisit(Group group ); /** * Called on visit to an entry * @param entry the entry being visited */ - void visit(E entry); + void visit(Entry entry); /** * called to determine whether to visit entries before subgroups, or not @@ -51,17 +51,16 @@ public interface Visitor , G extends Group, G extends Group, E extends Entry, I extends Icon> - implements Visitor { + abstract class Default > implements Visitor { @Override - public void startVisit(G group) {} + public void startVisit(Group group) {} @Override - public void endVisit(G group) {} + public void endVisit(Group group) {} @Override - public void visit(E entry) {} + public void visit(Entry entry) {} @Override public boolean isEntriesFirst() { @@ -72,8 +71,7 @@ public boolean isEntriesFirst() { /** * Visitor prints the Groups and Entries it visits to console */ - class Print , G extends Group, E extends Entry, I extends Icon> - extends Default { + class Print > extends Default { private final PrintStream printStream; @@ -86,12 +84,12 @@ public Print(PrintStream out) { } @Override - public void startVisit(G group) { + public void startVisit(Group group ) { printStream.println(group.toString()); } @Override - public void visit(E entry) { + public void visit(Entry entry) { printStream.println(entry.toString()); } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java index 7e17721c..e124d8f5 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java @@ -27,7 +27,7 @@ * * @author Jo */ -public abstract class AbstractDatabase, G extends Group, E extends Entry, I extends Icon> implements Database { +public abstract class AbstractDatabase> implements Database { private boolean isDirty; @@ -41,69 +41,69 @@ public void setDirty(boolean dirty) { } @Override - public void visit(Visitor visitor) { + public void visit(Visitor visitor) { visitor.startVisit(getRootGroup()); visit(getRootGroup(), visitor); visitor.endVisit(getRootGroup()); } @Override - public void visit(G group, Visitor visitor) { + public void visit(Group group, Visitor visitor) { if (visitor.isEntriesFirst()) { - for (E entry : group.getEntries()) { + for (Entry entry : group.getEntries()) { visitor.visit(entry); } } - for (G g : group.getGroups()) { + for (Group g : group.getGroups()) { visitor.startVisit(g); visit(g, visitor); visitor.endVisit(g); } if (!visitor.isEntriesFirst()) { - for (E entry : group.getEntries()) { + for (Entry entry : group.getEntries()) { visitor.visit(entry); } } } @Override - public List findEntries(Entry.Matcher matcher) { + public List> findEntries(Entry.Matcher matcher) { return getRootGroup().findEntries(matcher, true); } @Override - public List findEntries(String find) { + public List> findEntries(String find) { return getRootGroup().findEntries(find, true); } @Override - public G newGroup(String name) { - G result = newGroup(); + public Group newGroup(String name) { + Group result = newGroup(); result.setName(name); return result; } @Override - public G newGroup(Group group) { - G result = newGroup(); + public Group newGroup(Group group) { + Group result = newGroup(); result.setName(group.getName()); result.setIcon(this.newIcon(group.getIcon().getIndex())); return result; } @Override - public E newEntry(String title) { - E result = newEntry(); + public Entry newEntry(String title) { + Entry result = newEntry(); result.setTitle(title); return result; } @Override - public E newEntry(Entry entry) { - E result = newEntry(); + public Entry newEntry(Entry entry) { + Entry result = newEntry(); for (String propertyName: entry.getPropertyNames()) { try { // all implementations must support setting of STANDARD_PROPERTY_NAMES @@ -130,8 +130,8 @@ public E newEntry(Entry entry) { } @Override - public E findEntry(final UUID uuid) { - List entries = findEntries(entry -> entry.getUuid().equals(uuid)); + public Entry findEntry(final UUID uuid) { + List> entries = findEntries(entry -> entry.getUuid().equals(uuid)); if (entries.size() > 1) { throw new IllegalStateException("Two entries same UUID"); } @@ -143,7 +143,7 @@ public E findEntry(final UUID uuid) { @Override public boolean deleteEntry(final UUID uuid) { - E e = findEntry(uuid); + Entry e = findEntry(uuid); if (e == null) { return false; } @@ -158,13 +158,13 @@ public boolean deleteEntry(final UUID uuid) { } @Override - public G findGroup(final UUID uuid){ - final List groups = new ArrayList<>(); - visit(new Visitor.Default() { + public Group findGroup(final UUID uuid){ + final List> groups = new ArrayList<>(); + visit(new Visitor.Default() { // set to true while visiting sub groups of recycle bin boolean recycle; @Override - public void startVisit(G group) { + public void startVisit(Group group) { if (!recycle && group.getUuid().equals(uuid)) { groups.add(group); } @@ -174,7 +174,7 @@ public void startVisit(G group) { } @Override - public void endVisit(G group) { + public void endVisit(Group group) { if (group.isRecycleBin()) { recycle = false; } @@ -191,7 +191,7 @@ public void endVisit(G group) { @Override public boolean deleteGroup(final UUID uuid) { - G g = findGroup(uuid); + Group g = findGroup(uuid); if (g==null) { return false; } @@ -207,14 +207,14 @@ public boolean deleteGroup(final UUID uuid) { @Override public void emptyRecycleBin() { - G recycle = getRecycleBin(); + Group recycle = getRecycleBin(); if (recycle == null) { return; } - for (G g: recycle.getGroups()){ + for (Group g: recycle.getGroups()){ recycle.removeGroup(g); } - for (E e: recycle.getEntries()){ + for (Entry e: recycle.getEntries()){ recycle.removeEntry(e); } } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java index 50987d42..cf4a9f3a 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java @@ -23,7 +23,7 @@ * * @author Jo */ -public abstract class AbstractEntry, G extends Group, E extends Entry, I extends Icon> implements Entry { +public abstract class AbstractEntry> implements Entry { @Override public boolean matchTitle(String text){ @@ -57,7 +57,7 @@ public boolean match(Entry.Matcher matcher) { @Override public String getPath() { - G parent = this.getParent(); + Group parent = this.getParent(); String result = ""; if (parent != null) { result = parent.getPath(); diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java index cf13c0af..73c8ca41 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java @@ -19,7 +19,6 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.Icon; import java.util.ArrayList; import java.util.List; @@ -30,13 +29,13 @@ * * @author Jo */ -public abstract class AbstractGroup, G extends org.linguafranca.pwdb.base.AbstractGroup, E extends Entry, I extends Icon> implements Group { +public abstract class AbstractGroup> implements Group { @Override - public List findGroups(String group1) { - ArrayList result = new ArrayList<>(); - for (G g: getGroups()) { - if (g.getName().equals(group1)) { + public List> findGroups(String group) { + ArrayList> result = new ArrayList<>(); + for (Group g: getGroups()) { + if (g.getName().equals(group)) { result.add(g); } } @@ -44,7 +43,7 @@ public List findGroups(String group1) { } @Override - public List findEntries(String find, boolean recursive) { + public List> findEntries(String find, boolean recursive) { /* * Local helper class to avoid violating DRY in {@link AbstractGroup#findEntries(String, boolean)}. * Would-be lambda in Java 8. @@ -58,7 +57,7 @@ private TextMatcher(String text) { } @Override - public boolean matches(Entry entry) { + public boolean matches(Entry> entry) { return entry.match(text); } } @@ -67,15 +66,15 @@ public boolean matches(Entry entry) { } @Override - public List findEntries(Entry.Matcher matcher, boolean recursive) { - List result = new ArrayList<>(getEntries().size()); - for (E entry: getEntries()){ + public List> findEntries(Entry.Matcher matcher, boolean recursive) { + List > result = new ArrayList<>(getEntries().size()); + for (Entry entry: getEntries()){ if (entry.match(matcher)){ result.add(entry); } } if (recursive) { - for (G group : getGroups()) { + for (Group group: getGroups()) { // don't recurse into recycle bin if (group.isRecycleBin()) { continue; @@ -87,26 +86,26 @@ public List findEntries(Entry.Matcher matcher, boolean recursive) { } @Override - public void copy(Group parent) { - for (Group child : parent.getGroups()) { - G addedGroup = addGroup(this.getDatabase().newGroup(child)); + public void copy(Group parent) { + for (Group child : parent.getGroups()) { + Group addedGroup = addGroup(this.getDatabase().newGroup(child)); addedGroup.copy(child); } - for (Entry entry : parent.getEntries()) { + for (Entry entry : parent.getEntries()) { addEntry(this.getDatabase().newEntry(entry)); } } @Override public String getPath() { - Stack parents = new Stack<>(); - Group parent = this; + Stack> parents = new Stack<>(); + Group parent = this; parents.push(this); while ((parent=parent.getParent()) != null) { parents.push(parent); } StringBuilder result = new StringBuilder("/"); - while (parents.size() > 0) { + while (!parents.isEmpty()) { result.append(parents.pop().getName()).append("/"); } return result.toString(); diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index 7c50e16c..7e968f44 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -13,7 +13,7 @@ * establishes {@link org.linguafranca.pwdb.PropertyValue.StringStore} storage for unprotected values and * {@link org.linguafranca.pwdb.PropertyValue.SealedStore} for protected values. */ -public abstract class ProtectedDatabase, G extends Group, E extends Entry, I extends Icon> extends AbstractDatabase { +public abstract class ProtectedDatabase> extends AbstractDatabase { private PropertyValue.Strategy valueStrategy = new PropertyValue.Strategy.Default(); @Override diff --git a/dom/pom.xml b/dom/pom.xml deleted file mode 100644 index fa44bcb4..00000000 --- a/dom/pom.xml +++ /dev/null @@ -1,38 +0,0 @@ - - - - KeePassJava2-parent - org.linguafranca.pwdb - 2.2.3-SNAPSHOT - ../pom.xml - - 4.0.0 - - KeePassJava2-dom - KeePassJava2 :: DOM - - - - org.linguafranca.pwdb - KeePassJava2-kdbx - ${project.version} - - - org.linguafranca.pwdb - test - ${project.version} - test - - - junit - junit - test - - - org.linguafranca.pwdb - test - 2.2.3-SNAPSHOT - compile - - - \ No newline at end of file diff --git a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapper.java b/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapper.java deleted file mode 100644 index d6ba2513..00000000 --- a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapper.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.StreamConfiguration; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.base.AbstractDatabase; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpressionException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Objects; -import java.util.UUID; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.linguafranca.pwdb.kdbx.Helpers.base64FromUuid; -import static org.linguafranca.pwdb.kdbx.dom.DomHelper.*; - -/** - * The class wraps a {@link DomSerializableDatabase} as a {@link org.linguafranca.pwdb.Database}. - * - * @author jo - */ -public class DomDatabaseWrapper extends AbstractDatabase { - - private final DomSerializableDatabase domDatabase = DomSerializableDatabase.createEmptyDatabase(); - Element dbMeta; - private Document document; - private Element dbRootGroup; - - - private StreamFormat streamFormat; - - - public DomDatabaseWrapper() { - init(); - } - - /** - * load a database - * @param credentials credentials to use - * @param inputStream where to read from - * @return a database - */ - public DomDatabaseWrapper(Credentials credentials, InputStream inputStream) throws IOException { - this.streamFormat = new KdbxStreamFormat(); - streamFormat.load(domDatabase, credentials, inputStream); - init(); - } - - /** - * load a database - * @param credentials credentials to use - * @param inputStream where to read from - * @return a database - */ - public static DomDatabaseWrapper load(@NotNull Credentials credentials, - @NotNull InputStream inputStream) throws IOException { - return new DomDatabaseWrapper( - checkNotNull(credentials, "Credentials must not be null"), - checkNotNull(inputStream, "InputStream must not be null")); - } - - private void init() { - document = domDatabase.getDoc(); - try { - dbRootGroup = ((Element) DomHelper.xpath.evaluate("/KeePassFile/Root/Group", document, XPathConstants.NODE)); - dbMeta = ((Element) DomHelper.xpath.evaluate("/KeePassFile/Meta", document, XPathConstants.NODE)); - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - } - - /** - * Save database with same format/encryption settings as it was loaded with or V4 defaults if was not loaded - * @param credentials credentials to use - * @param outputStream where to write - */ - @Override - public void save(Credentials credentials, - OutputStream outputStream) throws IOException { - if (Objects.isNull(streamFormat)){ - streamFormat = new KdbxStreamFormat(new KdbxHeader(4)); - } - save(streamFormat, credentials, outputStream); - } - - /** - * Save database with same format/encryption settings as it was loaded with - * @param streamFormat format/config to use for saving - * @param credentials credentials to use - * @param outputStream where to write - */ - public void save(StreamFormat streamFormat, - Credentials credentials, - OutputStream outputStream) throws IOException { - DomHelper.getElement("//Generator", domDatabase.getDoc().getDocumentElement(), false).setTextContent("KeePassJava2-DOM"); - streamFormat.save(domDatabase, credentials, outputStream); - setDirty(false); - } - - public boolean shouldProtect(String name) { - Element protectionElement = DomHelper.getElement("MemoryProtection/Protect" + name, dbMeta, false); - if (protectionElement == null) { - return false; - } - return Boolean.parseBoolean(protectionElement.getTextContent()); - } - - @Override - public DomGroupWrapper getRootGroup() { - return new DomGroupWrapper(dbRootGroup, this, false); - } - - @Override - public DomGroupWrapper newGroup() { - return new DomGroupWrapper(document.createElement(DomHelper.GROUP_ELEMENT_NAME), this, true); - } - - @Override - public DomEntryWrapper newEntry() { - return new DomEntryWrapper(document.createElement(DomHelper.ENTRY_ELEMENT_NAME), this, true); - } - - @Override - public DomIconWrapper newIcon() { - return new DomIconWrapper(document.createElement(DomHelper.ICON_ELEMENT_NAME)); - } - - @Override - public DomIconWrapper newIcon(Integer i) { - DomIconWrapper icon = newIcon(); - icon.setIndex(i); - return icon; - } - - @Override - public DomGroupWrapper getRecycleBin() { - String UuidContent = getElementContent(RECYCLE_BIN_UUID_ELEMENT_NAME, dbMeta); - if (UuidContent != null) { - final UUID uuid = Helpers.uuidFromBase64(UuidContent); - if (uuid.getLeastSignificantBits() != 0 && uuid.getMostSignificantBits() != 0) { - for (DomGroupWrapper g : getRootGroup().getGroups()) { - if (g.getUuid().equals(uuid)) { - return g; - } - } - // the recycle bin seems to have been lost, better create another one - } - // uuid was 0 i.e. there isn't one - } - // no recycle bin group set up - if (!isRecycleBinEnabled()) { - return null; - } - - DomGroupWrapper g = newGroup(); - g.setName("Recycle Bin"); - getRootGroup().addGroup(g); - ensureElementContent(RECYCLE_BIN_UUID_ELEMENT_NAME, dbMeta, base64FromUuid(g.getUuid())); - touchElement(RECYCLE_BIN_CHANGED_ELEMENT_NAME, dbMeta); - return g; - } - - @Override - public boolean isRecycleBinEnabled() { - return Boolean.parseBoolean(getElementContent(RECYCLE_BIN_ENABLED_ELEMENT_NAME, dbMeta)); - } - - @Override - public void enableRecycleBin(boolean enable) { - setElementContent(RECYCLE_BIN_ENABLED_ELEMENT_NAME, dbMeta, ((Boolean) enable).toString()); - } - - public String getName() { - return DomHelper.getElementContent("DatabaseName", dbMeta); - } - - public void setName(String name) { - DomHelper.setElementContent("DatabaseName", dbMeta, name); - DomHelper.touchElement("DatabaseNameChanged", dbMeta); - setDirty(true); - } - - @Override - public String getDescription() { - return DomHelper.getElementContent("DatabaseDescription", dbMeta); - } - - @Override - public void setDescription(String description) { - DomHelper.setElementContent("DatabaseDescription", dbMeta, description); - DomHelper.touchElement("DatabaseDescriptionChanged", dbMeta); - setDirty(true); - } - public StreamFormat getStreamFormat() { - return streamFormat; - } -} diff --git a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomEntryWrapper.java b/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomEntryWrapper.java deleted file mode 100644 index 293b41c8..00000000 --- a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomEntryWrapper.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.base.AbstractEntry; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.w3c.dom.Element; - -import java.util.*; - -/** - * Class wraps Entries from a {@link DomSerializableDatabase} as {@link org.linguafranca.pwdb.Entry} - * - * @author jo - */ -public class DomEntryWrapper extends AbstractEntry { - - private static Map mandatoryEntryElements = new HashMap() {{ - put(DomHelper.UUID_ELEMENT_NAME, new DomHelper.UuidValueCreator()); - put(DomHelper.ICON_ELEMENT_NAME, new DomHelper.ConstantValueCreator("2")); - put(DomHelper.TIMES_ELEMENT_NAME, new DomHelper.ConstantValueCreator("")); - put(DomHelper.LAST_MODIFICATION_TIME_ELEMENT_NAME, new DomHelper.DateValueCreator()); - put(DomHelper.CREATION_TIME_ELEMENT_NAME, new DomHelper.DateValueCreator()); - put(DomHelper.LAST_ACCESS_TIME_ELEMENT_NAME, new DomHelper.DateValueCreator()); - put(DomHelper.EXPIRY_TIME_ELEMENT_NAME, new DomHelper.DateValueCreator()); - put(DomHelper.EXPIRES_ELEMENT_NAME, new DomHelper.ConstantValueCreator("False")); - put(DomHelper.USAGE_COUNT_ELEMENT_NAME, new DomHelper.ConstantValueCreator("0")); - put(DomHelper.LOCATION_CHANGED, new DomHelper.DateValueCreator()); - }}; - - final Element element; - private final DomDatabaseWrapper database; - - public DomEntryWrapper(Element element, DomDatabaseWrapper database, boolean newElement) { - this.element = element; - this.database = database; - if (newElement) { - DomHelper.ensureElements(element, mandatoryEntryElements); - ensureProperty("Notes"); - ensureProperty("Title"); - ensureProperty("URL"); - ensureProperty("UserName"); - ensureProperty("Password"); - } - } - - @Override - public String getProperty(String name) { - Element property = DomHelper.getElement(String.format(DomHelper.PROPERTY_ELEMENT_FORMAT, name), element, false); - if (property == null) { - return null; - } - return DomHelper.getElementContent(DomHelper.VALUE_ELEMENT_NAME, property); - } - - @Override - public void setPropertyValue(String name, String value) { - Element property = DomHelper.getElement(String.format(DomHelper.PROPERTY_ELEMENT_FORMAT, name), element, false); - if (property == null) { - property = DomHelper.newElement("String", element); - DomHelper.setElementContent("Key", property, name); - } - DomHelper.setElementContent(DomHelper.VALUE_ELEMENT_NAME, property, value); - DomHelper.touchElement(DomHelper.LAST_MODIFICATION_TIME_ELEMENT_NAME, element); - database.setDirty(true); - } - - @Override - public boolean removeProperty(String name) throws IllegalArgumentException { - if (STANDARD_PROPERTY_NAMES.contains(name)) throw new IllegalArgumentException("may not remove property: " + name); - boolean wasRemoved = DomHelper.removeElement(String.format(DomHelper.PROPERTY_ELEMENT_FORMAT, name), element); - if (wasRemoved) database.setDirty(true); - return wasRemoved; - } - - @Override - public List getPropertyNames() { - ArrayList result = new ArrayList<>(); - List list = DomHelper.getElements("String", element); - for (Element listElement: list) { - result.add(DomHelper.getElementContent("Key", listElement)); - } - return result; - } - - @Override - public byte[] getBinaryProperty(String name) { - Element property = DomHelper.getElement(String.format(DomHelper.BINARY_PROPERTY_ELEMENT_FORMAT, name), element, false); - if (property == null) { - return null; - } - return DomHelper.getBinaryElementContent(DomHelper.VALUE_ELEMENT_NAME, property); - } - - @Override - public void setBinaryProperty(String name, byte[] value) { - Element property = DomHelper.getElement(String.format(DomHelper.BINARY_PROPERTY_ELEMENT_FORMAT, name), element, false); - if (property == null) { - property = DomHelper.newElement("Binary", element); - DomHelper.setElementContent("Key", property, name); - } - DomHelper.setBinaryElementContent(DomHelper.VALUE_ELEMENT_NAME, property, value); - DomHelper.touchElement(DomHelper.LAST_MODIFICATION_TIME_ELEMENT_NAME, element); - database.setDirty(true); - - } - - @Override - public boolean removeBinaryProperty(String name) { - boolean wasRemoved = DomHelper.removeElement(String.format(DomHelper.BINARY_PROPERTY_ELEMENT_FORMAT, name), element); - if (wasRemoved) database.setDirty(true); - return wasRemoved; - } - - @Override - public List getBinaryPropertyNames() { - ArrayList result = new ArrayList<>(); - List list = DomHelper.getElements("Binary", element); - for (Element listElement: list) { - result.add(DomHelper.getElementContent("Key", listElement)); - } - return result; - } - - private void ensureProperty(String name){ - Element property = DomHelper.getElement(String.format(DomHelper.PROPERTY_ELEMENT_FORMAT, name), element, false); - if (property == null) { - Element container = DomHelper.newElement("String", element); - DomHelper.setElementContent("Key", container, name); - DomHelper.getElement("Value", container, true); - } - } - - @Override - public DomGroupWrapper getParent() { - if (element.getParentNode() == null) { - return null; - } - return new DomGroupWrapper((Element) element.getParentNode(), database, false); - } - - @Override - public UUID getUuid() { - return Helpers.uuidFromBase64(DomHelper.getElementContent(DomHelper.UUID_ELEMENT_NAME, element)); - } - - @Override - public DomIconWrapper getIcon() { - return new DomIconWrapper(DomHelper.getElement(DomHelper.ICON_ELEMENT_NAME, element, false)); - } - - @Override - public void setIcon(DomIconWrapper icon) { - DomHelper.getElement(DomHelper.ICON_ELEMENT_NAME, element, true).setTextContent(String.valueOf(icon.getIndex())); - DomHelper.touchElement(DomHelper.LAST_MODIFICATION_TIME_ELEMENT_NAME, element); - database.setDirty(true); - } - - @Override - public Date getLastAccessTime() { - return Helpers.toDate(DomHelper.getElementContent(DomHelper.LAST_ACCESS_TIME_ELEMENT_NAME, element)); - } - - @Override - public Date getCreationTime() { - return Helpers.toDate(DomHelper.getElementContent(DomHelper.CREATION_TIME_ELEMENT_NAME, element)); - } - - @Override - public boolean getExpires() { - String content = DomHelper.getElementContent(DomHelper.EXPIRES_ELEMENT_NAME, element); - return content != null && content.equalsIgnoreCase("true"); - } - - @Override - public void setExpires(boolean expires) { - DomHelper.setElementContent(DomHelper.EXPIRES_ELEMENT_NAME, element, expires ? "True" : "False"); - } - - @Override - public Date getExpiryTime() { - return Helpers.toDate(DomHelper.getElementContent(DomHelper.EXPIRY_TIME_ELEMENT_NAME, element)); - } - - @Override - public void setExpiryTime(Date expiryTime) throws IllegalArgumentException { - if (expiryTime == null) throw new IllegalArgumentException("expiryTime may not be null"); - String formatted = Helpers.fromDate(expiryTime); - DomHelper.setElementContent(DomHelper.EXPIRY_TIME_ELEMENT_NAME, element, formatted); - } - - @Override - public Date getLastModificationTime() { - return Helpers.toDate(DomHelper.getElementContent(DomHelper.LAST_MODIFICATION_TIME_ELEMENT_NAME, element)); - } - - @Override - protected void touch() { - DomHelper.setElementContent(DomHelper.LAST_MODIFICATION_TIME_ELEMENT_NAME, element, Helpers.fromDate(new Date())); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - DomEntryWrapper that = (DomEntryWrapper) o; - - return element.equals(that.element) && database.equals(that.database); - - } -} diff --git a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomGroupWrapper.java b/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomGroupWrapper.java deleted file mode 100644 index 504cf848..00000000 --- a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomGroupWrapper.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractGroup; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.w3c.dom.Element; - -import java.util.*; - -import static org.linguafranca.pwdb.kdbx.dom.DomHelper.*; - - -/** - * Class wraps Groups from a {@link DomSerializableDatabase} as {@link org.linguafranca.pwdb.Group} - * - * @author jo - */ -public class DomGroupWrapper extends AbstractGroup { - - static Map mandatoryGroupElements = new HashMap() {{ - put(UUID_ELEMENT_NAME, new UuidValueCreator()); - put(NAME_ELEMENT_NAME, new ConstantValueCreator("")); - put(NOTES_ELEMENT_NAME, new ConstantValueCreator("")); - put(ICON_ELEMENT_NAME, new ConstantValueCreator("0")); - put(TIMES_ELEMENT_NAME, new ConstantValueCreator("")); - put(LAST_MODIFICATION_TIME_ELEMENT_NAME, new DateValueCreator()); - put(CREATION_TIME_ELEMENT_NAME, new DateValueCreator()); - put(LAST_ACCESS_TIME_ELEMENT_NAME, new DateValueCreator()); - put(EXPIRY_TIME_ELEMENT_NAME, new DateValueCreator()); - put(EXPIRES_ELEMENT_NAME, new ConstantValueCreator("False")); - put(USAGE_COUNT_ELEMENT_NAME, new ConstantValueCreator("0")); - put(LOCATION_CHANGED, new DateValueCreator()); - }}; - - - private final Element element; - private final DomDatabaseWrapper database; - - public DomGroupWrapper(Element element, DomDatabaseWrapper database, boolean newGroup) { - this.database = database; - this.element = element; - if (newGroup) { - ensureElements(this.element, mandatoryGroupElements); - } - } - - @Override - public boolean isRootGroup() { - Element parent = ((Element) element.getParentNode()); - return parent != null && (parent.getTagName().equals("Root")); - } - - @Override - public boolean isRecycleBin() { - String UUIDcontent = getElementContent(RECYCLE_BIN_UUID_ELEMENT_NAME, database.dbMeta); - if (UUIDcontent != null){ - UUID uuid = Helpers.uuidFromBase64(UUIDcontent); - return uuid.equals(this.getUuid()); - } - return false; - } - - @Override - public @Nullable DomGroupWrapper getParent() { - Element parent = ((Element) element.getParentNode()); - if (parent == null) { - return null; - } - // if the element is the root group there is no parent - if (element == element.getOwnerDocument().getDocumentElement().getElementsByTagName(GROUP_ELEMENT_NAME).item(0)){ - return null; - } - return new DomGroupWrapper(parent, database, false); - } - - @Override - public void setParent(DomGroupWrapper parent) { - parent.addGroup(this); - } - - @Override - public List getGroups() { - List elements = getElements(GROUP_ELEMENT_NAME, this.element); - List result = new ArrayList<>(elements.size()); - for (Element e: elements){ - result.add(new DomGroupWrapper(e, database, false)); - } - return result; - } - - @Override - public int getGroupsCount() { - List elements = getElements(GROUP_ELEMENT_NAME, this.element); - return elements.size(); - } - - @Override - public DomGroupWrapper addGroup(DomGroupWrapper group) { - if (group.isRootGroup()) { - throw new IllegalStateException("Cannot set root group as child of another group"); - } - // skip if this is a new group with no parent - if (group.getParent() != null) { - group.getParent().touch(); - group.getParent().removeGroup(group); - } - element.appendChild(group.element); - touchElement("Times/LocationChanged", group.element); - touch(); - return group; - } - - @Override - public DomGroupWrapper removeGroup(DomGroupWrapper g1) { - element.removeChild(g1.element); - database.setDirty(true); - return g1; - } - - @Override - public List getEntries() { - List elements = getElements(ENTRY_ELEMENT_NAME, this.element); - List entries = new ArrayList<>(elements.size()); - for(Element e: elements) { - entries.add(new DomEntryWrapper(e, database, false)); - } - return entries; - } - - @Override - public int getEntriesCount() { - return getElementsCount(ENTRY_ELEMENT_NAME, this.element); - } - - @Override - public DomEntryWrapper addEntry(DomEntryWrapper entry) { - if (entry.getParent() != null) { - entry.element.getParentNode().removeChild(element); - } - element.appendChild(entry.element); - database.setDirty(true); - return entry; - } - - @Override - public DomEntryWrapper removeEntry(DomEntryWrapper e12) { - element.removeChild(e12.element); - database.setDirty(true); - return e12; - } - - @Override - public String getName() { - return getElementContent(NAME_ELEMENT_NAME, element); - } - - @Override - public void setName(String name) { - setElementContent(NAME_ELEMENT_NAME, element, name); - database.setDirty(true); - } - - @Override - public UUID getUuid() { - String encodedUuid = getElementContent(UUID_ELEMENT_NAME, element); - return Helpers.uuidFromBase64(encodedUuid); - } - - @Override - public DomIconWrapper getIcon() { - return new DomIconWrapper(getElement(ICON_ELEMENT_NAME, element, false)); - } - - @Override - public void setIcon(DomIconWrapper icon) { - setElementContent(ICON_ELEMENT_NAME, element, String.valueOf(icon.getIndex())); - database.setDirty(true); - } - - @NotNull - @Override - public DomDatabaseWrapper getDatabase() { - return database; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - DomGroupWrapper that = (DomGroupWrapper) o; - - return element.equals(that.element) && database.equals(that.database); - - } - - private void touch() { - touchElement(LAST_MODIFICATION_TIME_ELEMENT_NAME, this.element); - this.database.setDirty(true); - } -} diff --git a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomIconWrapper.java b/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomIconWrapper.java deleted file mode 100644 index b965e143..00000000 --- a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomIconWrapper.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.Icon; -import org.w3c.dom.Element; - -/** - * Class wraps Icons from a {@link DomSerializableDatabase} as {@link Icon} - * - * @author jo - */ -public class DomIconWrapper implements Icon { - - private final Element element; - - public DomIconWrapper(Element element) { - this.element = element; - } - - @Override - public int getIndex() { - return Integer.parseInt(element.getTextContent()); - } - - @Override - public void setIndex(int index) { - element.setTextContent(String.valueOf(index)); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || !(o instanceof Icon)) return false; - - Icon that = (Icon) o; - - return this.getIndex() == that.getIndex(); - - } -} diff --git a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomPropertyValueTest.java b/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomPropertyValueTest.java deleted file mode 100644 index c695129a..00000000 --- a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomPropertyValueTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.checks.PropertyValueChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class DomPropertyValueTest extends PropertyValueChecks { - - public DomPropertyValueTest() throws IOException { - super(false); - } - - - @Override - public Database createDatabase() throws IOException { - return new DomDatabaseWrapper(); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - try { - return DomDatabaseWrapper.load(credentials, inputStream); - } catch (Exception e) { - throw new IOException(e); - } - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } -} diff --git a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomSerializableDatabase.java b/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomSerializableDatabase.java deleted file mode 100644 index 9bfe4fce..00000000 --- a/dom/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomSerializableDatabase.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - - -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.SerializableDatabase; -import org.linguafranca.pwdb.security.StreamEncryptor; -import org.apache.commons.codec.binary.Base64; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.SAXException; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.*; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpressionException; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.time.Instant; -import java.util.Date; - -/** - * This class is an XML DOM implementation of a KDBX database. The data is maintained as a DOM, - * despite the obvious inefficiency of doing do, in order to maintain transparency on loading and - * saving of elements and attributes this implementation knows nothing about. - * - *

Obviously, perhaps, if the database is added to, or under certain types of modification, - * those elements will be missing from a re-serialization. - * - * @author jo - */ -public class DomSerializableDatabase implements SerializableDatabase { - - private Document doc; - private StreamEncryptor encryption; - - private DomSerializableDatabase() {} - - public static DomSerializableDatabase createEmptyDatabase() { - DomSerializableDatabase result = new DomSerializableDatabase(); - // read in the template KeePass XML database - try { - result.load(result.getClass().getClassLoader().getResourceAsStream("base.kdbx.xml")); - } catch (IOException e) { - throw new RuntimeException(e); - } - try { - // set the root group UUID - Node uuid = (Node) DomHelper.xpath.evaluate("//"+ DomHelper.UUID_ELEMENT_NAME, result.doc.getDocumentElement(), XPathConstants.NODE); - uuid.setTextContent(DomHelper.base64RandomUuid()); - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - return result; - } - - @Override - public SerializableDatabase load(InputStream inputStream) throws IOException { - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - try { - DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - doc = dBuilder.parse(inputStream); - - // we need to decrypt all protected fields - NodeList protectedContent = (NodeList) DomHelper.xpath.evaluate("//*[@Protected='True']", doc, XPathConstants.NODESET); - for (int i = 0; i < protectedContent.getLength(); i++){ - Element element = ((Element) protectedContent.item(i)); - String base64 = DomHelper.getElementContent(".", element); - // Android compatibility - byte[] encrypted = Base64.decodeBase64(base64.getBytes()); - String decrypted = new String(encryption.decrypt(encrypted), StandardCharsets.UTF_8); - DomHelper.setElementContent(".", element, decrypted); - element.removeAttribute("Protected"); - element.setAttribute("kpj2-ProtectOnOutput", "True"); - } - return this; - - } catch (ParserConfigurationException e) { - throw new IllegalStateException("Instantiating Document Builder", e); - } catch (SAXException e) { - throw new IllegalStateException("Parsing exception", e); - } catch (XPathExpressionException e) { - throw new IllegalStateException("XPath Exception", e); - } - } - - @Override - public void addBinary(int index, byte[] payload) { - DomHelper.addBinary(doc.getDocumentElement(), Helpers.encodeBase64Content(payload, true),index); - } - - @Override - public byte[] getBinary(int index) { - return Helpers.decodeBase64Content(DomHelper.getBinary(doc.getDocumentElement(), index).getBytes(StandardCharsets.UTF_8), true); - } - - @Override - public int getBinaryCount() { - return DomHelper.getBinaryCount(doc.getDocumentElement()); - } - - @Override - public void save(OutputStream outputStream) { - // make a copy so we can mess with content - Document copyDoc = (Document) doc.cloneNode(true); - try { - // check whether protection is required by default and if so mark the element with @@kpj2-ProtectOnOutput='True' - prepareProtection(copyDoc, "Title"); - prepareProtection(copyDoc, "UserName"); - prepareProtection(copyDoc, "Password"); - prepareProtection(copyDoc, "Notes"); - prepareProtection(copyDoc, "URL"); - - // encrypt and base64 every element marked as protected - NodeList protectedContent = (NodeList) DomHelper.xpath.evaluate("//*[@kpj2-ProtectOnOutput='True']", copyDoc, XPathConstants.NODESET); - for (int i = 0; i < protectedContent.getLength(); i++){ - Element element = ((Element) protectedContent.item(i)); - - element.removeAttribute("kpj2-ProtectOnOutput"); - element.setAttribute("Protected", "True"); - - String decrypted = DomHelper.getElementContent(".", element); - if (decrypted == null) { - decrypted = ""; - } - byte[] encrypted = encryption.encrypt(decrypted.getBytes()); - // Android compatibility - String base64 = new String(Base64.encodeBase64(encrypted)); - DomHelper.setElementContent(".", element, base64); - } - - // we need to serialise dates according to the format, and - // we are going to say that anything that is a date is called *Changed or *Time - // because that does work in the schema, but won't work for custom values - NodeList timeBasedContent = (NodeList) DomHelper.xpath.evaluate( - //"//*[ends-with(name(), 'Changed')] | //*[ends-with(name(), 'Time')]", - // dealing with XPath1 - "//*[substring(name(),string-length(name())-6) = 'Changed'] | //*[substring(name(),string-length(name())-3) = 'Time']", - copyDoc, - XPathConstants.NODESET); - for (int i = 0; i < timeBasedContent.getLength(); i++){ - Element element = ((Element) timeBasedContent.item(i)); - String time = DomHelper.getElementContent(".", element); - Date date; - if (time.equals("${creationDate}")) { - date = Date.from(Instant.now()); - } else { - date = Helpers.toDate(time); - } - String encoded = Helpers.fromDate(date); - DomHelper.setElementContent(".", element, encoded); - } - - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - - Source xmlSource = new DOMSource(copyDoc); - try { - TransformerFactory factory = TransformerFactory.newInstance(); - //factory.setAttribute("indent-number", "4"); - Transformer transformer = factory.newTransformer(); - transformer.setOutputProperty(OutputKeys.INDENT, "yes"); - //noinspection HttpUrlsUsage - transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4"); - transformer.transform(xmlSource, new StreamResult(outputStream)); - } catch (TransformerException e) { - throw new IllegalStateException(e); - } - } - - private static final String protectQuery = "//Meta/MemoryProtection/Protect%s"; - private static final String pattern = "//String/Key[text()='%s']/following-sibling::Value"; - private void prepareProtection(Document doc, String protect) throws XPathExpressionException { - // does this require encryption - String query = String.format(protectQuery, protect); - if (!((String) DomHelper.xpath.evaluate(query, doc, XPathConstants.STRING)).equalsIgnoreCase("true")) { - return; - } - // mark the field as Protected but don't actually encrypt yet, that comes later - String path = String.format(pattern, protect); - NodeList nodelist = (NodeList) DomHelper.xpath.evaluate(path, doc, XPathConstants.NODESET); - for (int i = 0; i < nodelist.getLength(); i++) { - Element element = (Element) nodelist.item(i); - element.setAttribute("kpj2-ProtectOnOutput", "True"); - } - } - - @Override - public byte[] getHeaderHash() { - try { - String base64 = (String) DomHelper.xpath.evaluate("//HeaderHash", doc, XPathConstants.STRING); - // Android compatibility - return Base64.decodeBase64(base64.getBytes()); - } catch (XPathExpressionException e) { - throw new IllegalStateException("Can't get header hash", e); - } - } - - @Override - public void setHeaderHash(byte[] hash) { - // Android compatibility - String base64String = new String(Base64.encodeBase64(hash)); - DomHelper.ensureElementContent("Meta/HeaderHash", doc.getDocumentElement(), base64String); - //((Element) DomHelper.xpath.evaluate("//HeaderHash", doc, XPathConstants.NODE)).setTextContent(base64String); - } - - - @Override - public StreamEncryptor getEncryption() { - return encryption; - } - - @Override - public void setEncryption(StreamEncryptor encryption) { - this.encryption = encryption; - } - - public Document getDoc() { - return doc; - } -} diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV3Test.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV3Test.java deleted file mode 100644 index 58ccdebc..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV3Test.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.Credentials; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -public class DomBinaryPropertyV3Test extends BinaryPropertyChecks { - - public DomBinaryPropertyV3Test() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); - database = DomDatabaseWrapper.load(new KdbxCreds("123".getBytes()), inputStream); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return DomDatabaseWrapper.load(credentials, inputStream); - } - - @Override - public Database newDatabase() { - return new DomDatabaseWrapper(); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } -} \ No newline at end of file diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV4Test.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV4Test.java deleted file mode 100644 index 97499e18..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomBinaryPropertyV4Test.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -public class DomBinaryPropertyV4Test extends BinaryPropertyChecks { - - public DomBinaryPropertyV4Test() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-Argon2-Attachment.kdbx"); - database = DomDatabaseWrapper.load(new KdbxCreds("123".getBytes()), inputStream); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - StreamFormat sf = new KdbxStreamFormat(new KdbxHeader(4)); - database.save(sf, credentials, outputStream); - } - - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return DomDatabaseWrapper.load(credentials, inputStream); - } - - @Override - public Database newDatabase() { - return new DomDatabaseWrapper(); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - -} \ No newline at end of file diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperLoaderTest.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperLoaderTest.java deleted file mode 100644 index e980f98a..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperLoaderTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.checks.DatabaseLoaderChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.Credentials; - -import java.io.IOException; -import java.io.InputStream; - -/** - * @author jo - */ -public class DomDatabaseWrapperLoaderTest extends DatabaseLoaderChecks { - - public DomDatabaseWrapperLoaderTest() throws IOException { - // get an input stream from kdbx file - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - // file has password credentials - Credentials credentials = new KdbxCreds("123".getBytes()); - // open database. DomDatabaseWrapper is so-called, since it wraps - // a W3C DOM, populated from the KeePass XML, and presents it - // through a org.linguafranca.keepass.Database interface. - super.database = DomDatabaseWrapper.load(credentials, inputStream); - } -} diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperTest.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperTest.java deleted file mode 100644 index aef78d3d..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomDatabaseWrapperTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.checks.BasicDatabaseChecks; -import org.junit.Test; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.Credentials; - -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintStream; - -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class DomDatabaseWrapperTest extends BasicDatabaseChecks { - - static PrintStream printStream = getTestPrintStream(); - - public DomDatabaseWrapperTest() throws IOException { - } - - @Test - public void inspectPasswordDatabase() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - DomDatabaseWrapper database = new DomDatabaseWrapper(new KdbxCreds("123".getBytes()), inputStream); - - database.save(new StreamFormat.None(), new Credentials.None(), printStream); - } - - @Test - public void inspectKeyfileDatabase() throws IOException { - InputStream keyFileInputStream = getClass().getClassLoader().getResourceAsStream("KeyFileDatabase.key"); - Credentials credentials = new KdbxCreds("123".getBytes(), keyFileInputStream); - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("KeyFileDatabase.kdbx"); - DomDatabaseWrapper database = new DomDatabaseWrapper(credentials, inputStream); - - database.save(new StreamFormat.None(), new Credentials.None(), printStream); - } - - @Override - public Database createDatabase() throws IOException { - return new DomDatabaseWrapper(); - } -} \ No newline at end of file diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomHelperTest.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomHelperTest.java deleted file mode 100644 index 227b5e3e..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomHelperTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.junit.Test; -import org.linguafranca.pwdb.kdbx.Helpers; - -import java.io.PrintStream; - -import static org.junit.Assert.assertEquals; -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class DomHelperTest { - - static PrintStream printStream = getTestPrintStream(); - - @Test - public void testBase64RandomUuid() throws Exception { - // just check that it can do something - String uuid1 = DomHelper.base64RandomUuid(); - printStream.println(Helpers.uuidFromBase64(uuid1)); - String uuid2 = DomHelper.base64RandomUuid(); - printStream.println(Helpers.uuidFromBase64(uuid2)); - } -} \ No newline at end of file diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomRecycleBinTest.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomRecycleBinTest.java deleted file mode 100644 index 9502ff6e..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomRecycleBinTest.java +++ /dev/null @@ -1,14 +0,0 @@ -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.checks.RecycleBinChecks; - -import java.io.IOException; - -/** - * @author jo - */ -public class DomRecycleBinTest extends RecycleBinChecks { - public DomRecycleBinTest() throws IOException { - database = new DomDatabaseWrapper(); - } -} diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomSaveAndReloadTest.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomSaveAndReloadTest.java deleted file mode 100644 index 704f0a72..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomSaveAndReloadTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.checks.SaveAndReloadChecks; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -public class DomSaveAndReloadTest extends SaveAndReloadChecks { - - @Override - public Database getDatabase() { - return new DomDatabaseWrapper(); - } - @Override - public Database getDatabase(String name, Credentials credentials) throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name); - return DomDatabaseWrapper.load(credentials, inputStream); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return DomDatabaseWrapper.load(credentials, inputStream); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds("123".getBytes()); - } - - @Override - public boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2) { - KdbxHeader h1 = (KdbxHeader) s1.getStreamConfiguration(); - KdbxHeader h2 = (KdbxHeader) s1.getStreamConfiguration(); - return (h1.getVersion() == h2.getVersion() && - h1.getProtectedStreamAlgorithm().equals(h2.getProtectedStreamAlgorithm()) && - h1.getKeyDerivationFunction().equals(h2.getKeyDerivationFunction()) && - h1.getCipherAlgorithm().equals(h2.getCipherAlgorithm())); - } -} diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomV4Test.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomV4Test.java deleted file mode 100644 index d3ad03a2..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/DomV4Test.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.linguafranca.pwdb.kdbx.dom; - -import org.junit.Test; -import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.InputStream; -import java.io.PrintStream; - -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class DomV4Test { - - static PrintStream printStream = getTestPrintStream(); - - @Test - public void loadKdbxV4() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); - DomDatabaseWrapper database = DomDatabaseWrapper.load(new KdbxCreds("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); - // test what happens to dates in V4 - database.visit(new Visitor.Default(){ - - @Override - public void visit(DomEntryWrapper entry) { - printStream.println(entry.getCreationTime()); - } - }); - } - -} diff --git a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/VisitorTest.java b/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/VisitorTest.java deleted file mode 100644 index 199c78b9..00000000 --- a/dom/src/test/java/org/linguafranca/pwdb/kdbx/dom/VisitorTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.junit.Assert; -import org.junit.Test; -import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; - -import java.io.InputStream; -import java.io.PrintStream; -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertTrue; -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author Jo - */ -public class VisitorTest { - - static PrintStream printStream = getTestPrintStream(); - - private List visitorList = new ArrayList<>(); - private Visitor visitor = new Visitor() { - StringBuffer indentation = new StringBuffer(); - @Override - public void startVisit(Group group) { - printStream.println(indentation.toString() + group.getName()); - indentation.append(" "); - } - - @Override - public void endVisit(Group group) { - indentation.setLength(indentation.length()-3); - } - - @Override - public boolean isEntriesFirst() { - return false; - } - - @Override - public void visit(Entry entry) { - printStream.println(indentation.toString() + "= " + entry.getTitle()); - visitorList.add(entry); - } - }; - - @Test - public void testLoadDB() { - try { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - DomDatabaseWrapper db = new DomDatabaseWrapper(new KdbxCreds("123".getBytes()), inputStream); - db.visit(visitor); - - //noinspection unchecked - List matched = db.findEntries(new Entry.Matcher() { - String matchee = ""; - @Override - public boolean matches(Entry entry) { - return entry.getTitle().toLowerCase().contains(matchee) || - entry.getNotes().toLowerCase().contains(matchee) || - entry.getUsername().toLowerCase().contains(matchee); - } - }); - - Assert.assertTrue(matched.size() == visitorList.size()); - - for (Entry e: matched) { - Assert.assertTrue(visitorList.contains(e)); - } - } catch (Exception e) { - Assert.assertTrue("Couldn\'t open test DB " + e.getMessage(), false); - } - } -} diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java index d17615d5..2b38e66f 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java @@ -34,7 +34,7 @@ import static org.linguafranca.pwdb.kdbx.jackson.JacksonSerializableDatabase.createEmptyDatabase; -public class JacksonDatabase extends ProtectedDatabase { +public class JacksonDatabase extends ProtectedDatabase { KeePassFile keePassFile; StreamFormat streamFormat; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java index 897e7952..6755f7be 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java @@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull; +import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.base.AbstractEntry; import org.linguafranca.pwdb.kdbx.Helpers; @@ -64,7 +65,7 @@ @JsonIgnoreProperties({"path", "username", "title", "notes", "url", "password"}) -public class JacksonEntry extends AbstractEntry { +public class JacksonEntry extends AbstractEntry { @JacksonXmlProperty(localName = "UUID") @@ -287,7 +288,7 @@ public JacksonIcon getIcon() { } @Override - public void setIcon(JacksonIcon icon) { + public void setIcon(Icon icon) { iconID = icon.getIndex(); } diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java index f71ffb3d..5076aa13 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java @@ -24,6 +24,8 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import org.jetbrains.annotations.NotNull; +import org.linguafranca.pwdb.Group; +import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.base.AbstractGroup; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; @@ -52,7 +54,7 @@ "group", }) @JsonIgnoreProperties(ignoreUnknown=true) -public class JacksonGroup extends AbstractGroup { +public class JacksonGroup extends AbstractGroup { @JacksonXmlProperty(localName = "UUID") @JsonDeserialize(converter = Base64ToUUIDConverter.class) @@ -257,7 +259,7 @@ public JacksonIcon getIcon() { } @Override - public void setIcon(JacksonIcon icon) { + public void setIcon(Icon icon) { this.iconID = icon.getIndex(); touch(); } diff --git a/jaxb/bindings.xml b/jaxb/bindings.xml deleted file mode 100644 index c6907af8..00000000 --- a/jaxb/bindings.xml +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - This class, while generated, is not used see {@link - org.linguafranca.pwdb.kdbx.jaxb.org.linguafranca.pwdb.base.AbstractJaxbParentedBinding} which is the - actual implementation class. - - - - - - - - - - - - \ No newline at end of file diff --git a/jaxb/gradle-source-sets.txt b/jaxb/gradle-source-sets.txt deleted file mode 100644 index 195f0d4f..00000000 --- a/jaxb/gradle-source-sets.txt +++ /dev/null @@ -1,18 +0,0 @@ -sourceSets { - main { - java { - srcDirs = ['src/main/java', 'src/generated/java'] - } - resources { - srcDirs = ['src/main/resources'] - } - } - test{ - java { - srcDirs = ['src/test/java'] - } - resources { - srcDirs = ['src/test/resources'] - } - } -} \ No newline at end of file diff --git a/jaxb/pom.xml b/jaxb/pom.xml deleted file mode 100644 index 535b53de..00000000 --- a/jaxb/pom.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - - - KeePassJava2-parent - org.linguafranca.pwdb - 2.2.3-SNAPSHOT - ../pom.xml - - 4.0.0 - - KeePassJava2-jaxb - KeePassJava2 :: JAXB - Contains a JAXB implementation for KDBX. - - - - org.linguafranca.pwdb - KeePassJava2-kdbx - ${project.version} - - - - javax.xml.bind - jaxb-api - 2.3.1 - - - com.sun.xml.bind - jaxb-impl - 2.3.9 - - - org.linguafranca.pwdb - test - ${project.version} - test - - - junit - junit - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - src/generated/java - - - - - - - - generateJaxbSources - - - - - - org.codehaus.mojo - jaxb2-maven-plugin - 2.2 - - - xjc - - xjc - - - - - - ../KDBX.4.xsd - - - bindings.xml - - org.linguafranca.pwdb.kdbx.jaxb.binding - src/generated/java - - - - - - - - \ No newline at end of file diff --git a/jaxb/src/generated/java/META-INF/sun-jaxb.episode b/jaxb/src/generated/java/META-INF/sun-jaxb.episode deleted file mode 100644 index c585a686..00000000 --- a/jaxb/src/generated/java/META-INF/sun-jaxb.episode +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter1.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter1.java deleted file mode 100644 index 16005b3b..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter1.java +++ /dev/null @@ -1,27 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.Date; -import javax.xml.bind.annotation.adapters.XmlAdapter; - -public class Adapter1 - extends XmlAdapter -{ - - - public Date unmarshal(String value) { - return (org.linguafranca.pwdb.kdbx.jaxb.util.DateAdapter.fromString(value)); - } - - public String marshal(Date value) { - return (org.linguafranca.pwdb.kdbx.jaxb.util.DateAdapter.toString(value)); - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter2.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter2.java deleted file mode 100644 index 530b9b09..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter2.java +++ /dev/null @@ -1,26 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import javax.xml.bind.annotation.adapters.XmlAdapter; - -public class Adapter2 - extends XmlAdapter -{ - - - public Boolean unmarshal(String value) { - return (org.linguafranca.pwdb.kdbx.Helpers.toBoolean(value)); - } - - public String marshal(Boolean value) { - return (org.linguafranca.pwdb.kdbx.Helpers.fromBoolean(value)); - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter3.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter3.java deleted file mode 100644 index aa3295be..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Adapter3.java +++ /dev/null @@ -1,27 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.UUID; -import javax.xml.bind.annotation.adapters.XmlAdapter; - -public class Adapter3 - extends XmlAdapter -{ - - - public UUID unmarshal(String value) { - return (org.linguafranca.pwdb.kdbx.Helpers.uuidFromBase64(value)); - } - - public String marshal(UUID value) { - return (org.linguafranca.pwdb.kdbx.Helpers.base64FromUuid(value)); - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/AutoType.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/AutoType.java deleted file mode 100644 index fe87bdea..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/AutoType.java +++ /dev/null @@ -1,239 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.ArrayList; -import java.util.List; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlElementRef; -import javax.xml.bind.annotation.XmlElementRefs; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - - -/** - * Stores everything related to auto-type for this wrappedEntry. - * http://keepass.info/help/base/autotype.html - * - * - *

Java class for autoType complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="autoType">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="Enabled" type="{}keepassBoolean"/>
- *         <element name="DataTransferObfuscation">
- *           <simpleType>
- *             <restriction base="{http://www.w3.org/2001/XMLSchema}int">
- *               <enumeration value="0"/>
- *               <enumeration value="1"/>
- *             </restriction>
- *           </simpleType>
- *         </element>
- *         <element name="DefaultSequence" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *         <element name="Association">
- *           <complexType>
- *             <complexContent>
- *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 <sequence maxOccurs="unbounded" minOccurs="0">
- *                   <element name="Window" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *                   <element name="KeystrokeSequence" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *                 </sequence>
- *               </restriction>
- *             </complexContent>
- *           </complexType>
- *         </element>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "autoType", propOrder = { - "enabled", - "dataTransferObfuscation", - "defaultSequence", - "association" -}) -public class AutoType { - - @XmlElement(name = "Enabled", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean enabled; - @XmlElement(name = "DataTransferObfuscation") - protected int dataTransferObfuscation; - @XmlElement(name = "DefaultSequence", required = true) - protected String defaultSequence; - @XmlElement(name = "Association", required = true) - protected AutoType.Association association; - - /** - * Gets the value of the enabled property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getEnabled() { - return enabled; - } - - /** - * Sets the value of the enabled property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setEnabled(Boolean value) { - this.enabled = value; - } - - /** - * Gets the value of the dataTransferObfuscation property. - * - */ - public int getDataTransferObfuscation() { - return dataTransferObfuscation; - } - - /** - * Sets the value of the dataTransferObfuscation property. - * - */ - public void setDataTransferObfuscation(int value) { - this.dataTransferObfuscation = value; - } - - /** - * Gets the value of the defaultSequence property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getDefaultSequence() { - return defaultSequence; - } - - /** - * Sets the value of the defaultSequence property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDefaultSequence(String value) { - this.defaultSequence = value; - } - - /** - * Gets the value of the association property. - * - * @return - * possible object is - * {@link AutoType.Association } - * - */ - public AutoType.Association getAssociation() { - return association; - } - - /** - * Sets the value of the association property. - * - * @param value - * allowed object is - * {@link AutoType.Association } - * - */ - public void setAssociation(AutoType.Association value) { - this.association = value; - } - - - /** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-     * <complexType>
-     *   <complexContent>
-     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *       <sequence maxOccurs="unbounded" minOccurs="0">
-     *         <element name="Window" type="{http://www.w3.org/2001/XMLSchema}string"/>
-     *         <element name="KeystrokeSequence" type="{http://www.w3.org/2001/XMLSchema}string"/>
-     *       </sequence>
-     *     </restriction>
-     *   </complexContent>
-     * </complexType>
-     * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "windowAndKeystrokeSequence" - }) - public static class Association { - - @XmlElementRefs({ - @XmlElementRef(name = "KeystrokeSequence", type = JAXBElement.class, required = false), - @XmlElementRef(name = "Window", type = JAXBElement.class, required = false) - }) - protected List> windowAndKeystrokeSequence; - - /** - * Gets the value of the windowAndKeystrokeSequence property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the windowAndKeystrokeSequence property. - * - *

- * For example, to add a new item, do as follows: - *

-         *    getWindowAndKeystrokeSequence().add(newItem);
-         * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link JAXBElement }{@code <}{@link String }{@code >} - * {@link JAXBElement }{@code <}{@link String }{@code >} - * - * - */ - public List> getWindowAndKeystrokeSequence() { - if (windowAndKeystrokeSequence == null) { - windowAndKeystrokeSequence = new ArrayList>(); - } - return this.windowAndKeystrokeSequence; - } - - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Binaries.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Binaries.java deleted file mode 100644 index 4090b908..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Binaries.java +++ /dev/null @@ -1,201 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.ArrayList; -import java.util.List; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - - -/** - * Binary field for elements are centrally stored in this element. - * The same Binary element may be referenced from many Entries. - * - * - *

Java class for binaries complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="binaries">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence maxOccurs="unbounded" minOccurs="0">
- *         <element name="Binary">
- *           <complexType>
- *             <simpleContent>
- *               <extension base="<http://www.w3.org/2001/XMLSchema>base64Binary">
- *                 <attribute name="ID" type="{http://www.w3.org/2001/XMLSchema}int" />
- *                 <attribute name="Compressed" type="{}keepassBoolean" default="False" />
- *               </extension>
- *             </simpleContent>
- *           </complexType>
- *         </element>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "binaries", propOrder = { - "binary" -}) -public class Binaries { - - @XmlElement(name = "Binary") - protected List binary; - - /** - * Gets the value of the binary property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the binary property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getBinary().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Binaries.Binary } - * - * - */ - public List getBinary() { - if (binary == null) { - binary = new ArrayList(); - } - return this.binary; - } - - - /** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-     * <complexType>
-     *   <simpleContent>
-     *     <extension base="<http://www.w3.org/2001/XMLSchema>base64Binary">
-     *       <attribute name="ID" type="{http://www.w3.org/2001/XMLSchema}int" />
-     *       <attribute name="Compressed" type="{}keepassBoolean" default="False" />
-     *     </extension>
-     *   </simpleContent>
-     * </complexType>
-     * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "value" - }) - public static class Binary { - - @XmlValue - protected byte[] value; - @XmlAttribute(name = "ID") - protected Integer id; - @XmlAttribute(name = "Compressed") - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean compressed; - - /** - * Gets the value of the value property. - * - * @return - * possible object is - * byte[] - */ - public byte[] getValue() { - return value; - } - - /** - * Sets the value of the value property. - * - * @param value - * allowed object is - * byte[] - */ - public void setValue(byte[] value) { - this.value = value; - } - - /** - * Gets the value of the id property. - * - * @return - * possible object is - * {@link Integer } - * - */ - public Integer getID() { - return id; - } - - /** - * Sets the value of the id property. - * - * @param value - * allowed object is - * {@link Integer } - * - */ - public void setID(Integer value) { - this.id = value; - } - - /** - * Gets the value of the compressed property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getCompressed() { - if (compressed == null) { - return new Adapter2().unmarshal("False"); - } else { - return compressed; - } - } - - /** - * Sets the value of the compressed property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setCompressed(Boolean value) { - this.compressed = value; - } - - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/BinaryField.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/BinaryField.java deleted file mode 100644 index 50581d10..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/BinaryField.java +++ /dev/null @@ -1,164 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlType; - - -/** - * If derived from a file then the Key element conventionally contains the file name. - * I think it would be useful to add a mime type on this ... - * - * The Value element is the value of /./meta/binaries/binary/@ID that is referenced from this - * binary. - * - * - *

Java class for binaryField complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="binaryField">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="Key" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *         <element name="Value">
- *           <complexType>
- *             <complexContent>
- *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 <attribute name="Ref" type="{http://www.w3.org/2001/XMLSchema}int" />
- *               </restriction>
- *             </complexContent>
- *           </complexType>
- *         </element>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "binaryField", propOrder = { - "key", - "value" -}) -public class BinaryField { - - @XmlElement(name = "Key", required = true) - protected String key; - @XmlElement(name = "Value", required = true) - protected BinaryField.Value value; - - /** - * Gets the value of the key property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getKey() { - return key; - } - - /** - * Sets the value of the key property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setKey(String value) { - this.key = value; - } - - /** - * Gets the value of the value property. - * - * @return - * possible object is - * {@link BinaryField.Value } - * - */ - public BinaryField.Value getValue() { - return value; - } - - /** - * Sets the value of the value property. - * - * @param value - * allowed object is - * {@link BinaryField.Value } - * - */ - public void setValue(BinaryField.Value value) { - this.value = value; - } - - - /** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-     * <complexType>
-     *   <complexContent>
-     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *       <attribute name="Ref" type="{http://www.w3.org/2001/XMLSchema}int" />
-     *     </restriction>
-     *   </complexContent>
-     * </complexType>
-     * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "") - public static class Value { - - @XmlAttribute(name = "Ref") - protected Integer ref; - - /** - * Gets the value of the ref property. - * - * @return - * possible object is - * {@link Integer } - * - */ - public Integer getRef() { - return ref; - } - - /** - * Sets the value of the ref property. - * - * @param value - * allowed object is - * {@link Integer } - * - */ - public void setRef(Integer value) { - this.ref = value; - } - - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomData.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomData.java deleted file mode 100644 index cdd94c65..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomData.java +++ /dev/null @@ -1,80 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.ArrayList; -import java.util.List; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAnyElement; -import javax.xml.bind.annotation.XmlType; - - -/** - * Third party programs and plugins can put custom data here. - * Unique element names should be used, e.g. "PluginName_ItemName". - * - * - *

Java class for customData complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="customData">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <any maxOccurs="unbounded" minOccurs="0"/>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "customData", propOrder = { - "any" -}) -public class CustomData { - - @XmlAnyElement(lax = true) - protected List any; - - /** - * Gets the value of the any property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the any property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getAny().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link Object } - * - * - */ - public List getAny() { - if (any == null) { - any = new ArrayList(); - } - return this.any; - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomIcons.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomIcons.java deleted file mode 100644 index aa4b00d8..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/CustomIcons.java +++ /dev/null @@ -1,234 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlSchemaType; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - - -/** - *

Java class for customIcons complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="customIcons">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence maxOccurs="unbounded">
- *         <element name="Icon">
- *           <complexType>
- *             <complexContent>
- *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 <sequence>
- *                   <element name="UUID" type="{}uuid"/>
- *                   <element name="Name" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *                   <element name="LastModificationTime" type="{}keepassDateTime"/>
- *                   <element name="Data" type="{http://www.w3.org/2001/XMLSchema}base64Binary"/>
- *                 </sequence>
- *               </restriction>
- *             </complexContent>
- *           </complexType>
- *         </element>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "customIcons", propOrder = { - "icon" -}) -public class CustomIcons { - - @XmlElement(name = "Icon", required = true) - protected List icon; - - /** - * Gets the value of the icon property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the icon property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getIcon().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link CustomIcons.Icon } - * - * - */ - public List getIcon() { - if (icon == null) { - icon = new ArrayList(); - } - return this.icon; - } - - - /** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-     * <complexType>
-     *   <complexContent>
-     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *       <sequence>
-     *         <element name="UUID" type="{}uuid"/>
-     *         <element name="Name" type="{http://www.w3.org/2001/XMLSchema}string"/>
-     *         <element name="LastModificationTime" type="{}keepassDateTime"/>
-     *         <element name="Data" type="{http://www.w3.org/2001/XMLSchema}base64Binary"/>
-     *       </sequence>
-     *     </restriction>
-     *   </complexContent>
-     * </complexType>
-     * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "uuid", - "name", - "lastModificationTime", - "data" - }) - public static class Icon { - - @XmlElement(name = "UUID", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID uuid; - @XmlElement(name = "Name", required = true) - protected String name; - @XmlElement(name = "LastModificationTime", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date lastModificationTime; - @XmlElement(name = "Data", required = true) - protected byte[] data; - - /** - * Gets the value of the uuid property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getUUID() { - return uuid; - } - - /** - * Sets the value of the uuid property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setUUID(UUID value) { - this.uuid = value; - } - - /** - * Gets the value of the name property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getName() { - return name; - } - - /** - * Sets the value of the name property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setName(String value) { - this.name = value; - } - - /** - * Gets the value of the lastModificationTime property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getLastModificationTime() { - return lastModificationTime; - } - - /** - * Sets the value of the lastModificationTime property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setLastModificationTime(Date value) { - this.lastModificationTime = value; - } - - /** - * Gets the value of the data property. - * - * @return - * possible object is - * byte[] - */ - public byte[] getData() { - return data; - } - - /** - * Sets the value of the data property. - * - * @param value - * allowed object is - * byte[] - */ - public void setData(byte[] value) { - this.data = value; - } - - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/History.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/History.java deleted file mode 100644 index d8110ad0..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/History.java +++ /dev/null @@ -1,71 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlType; - - -/** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType>
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element ref="{}Entry"/>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = { - "entry" -}) -@XmlRootElement(name = "History") -public class History { - - @XmlElement(name = "Entry", required = true) - protected JaxbEntryBinding entry; - - /** - * Gets the value of the entry property. - * - * @return - * possible object is - * {@link JaxbEntryBinding } - * - */ - public JaxbEntryBinding getEntry() { - return entry; - } - - /** - * Sets the value of the entry property. - * - * @param value - * allowed object is - * {@link JaxbEntryBinding } - * - */ - public void setEntry(JaxbEntryBinding value) { - this.entry = value; - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbEntryBinding.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbEntryBinding.java deleted file mode 100644 index 0f6d2c88..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbEntryBinding.java +++ /dev/null @@ -1,423 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlSchemaType; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.linguafranca.pwdb.kdbx.jaxb.base.AbstractJaxbParentedBinding; - - -/** - *

Java class for Entry element declaration. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <element name="Entry">
- *   <complexType>
- *     <complexContent>
- *       <extension base="{}Parented">
- *         <sequence>
- *           <element name="UUID" type="{}uuid"/>
- *           <element name="IconID" type="{}iconId"/>
- *           <element name="CustomIconUUID" type="{}customIconUuidRef" minOccurs="0"/>
- *           <element name="ForegroundColor" type="{}color"/>
- *           <element name="BackgroundColor" type="{}color"/>
- *           <element name="OverrideURL" type="{}overrideUrl"/>
- *           <element name="Tags" type="{}commaSeparatedList"/>
- *           <element ref="{}Times"/>
- *           <element name="String" type="{}stringField" maxOccurs="unbounded" minOccurs="0"/>
- *           <element name="Binary" type="{}binaryField" maxOccurs="unbounded" minOccurs="0"/>
- *           <element name="AutoType" type="{}autoType"/>
- *           <element ref="{}History" minOccurs="0"/>
- *           <element name="CustomData" type="{}customData" minOccurs="0"/>
- *         </sequence>
- *       </extension>
- *     </complexContent>
- *   </complexType>
- * </element>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = { - "uuid", - "iconID", - "customIconUUID", - "foregroundColor", - "backgroundColor", - "overrideURL", - "tags", - "times", - "string", - "binary", - "autoType", - "history", - "customData" -}) -@XmlRootElement(name = "Entry") -public class JaxbEntryBinding - extends AbstractJaxbParentedBinding -{ - - @XmlElement(name = "UUID", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID uuid; - @XmlElement(name = "IconID") - protected int iconID; - @XmlElement(name = "CustomIconUUID", type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID customIconUUID; - @XmlElement(name = "ForegroundColor", required = true) - protected String foregroundColor; - @XmlElement(name = "BackgroundColor", required = true) - protected String backgroundColor; - @XmlElement(name = "OverrideURL", required = true) - protected String overrideURL; - @XmlElement(name = "Tags", required = true) - protected String tags; - @XmlElement(name = "Times", required = true) - protected Times times; - @XmlElement(name = "String") - protected List string; - @XmlElement(name = "Binary") - protected List binary; - @XmlElement(name = "AutoType", required = true) - protected AutoType autoType; - @XmlElement(name = "History") - protected History history; - @XmlElement(name = "CustomData") - protected CustomData customData; - - /** - * Gets the value of the uuid property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getUUID() { - return uuid; - } - - /** - * Sets the value of the uuid property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setUUID(UUID value) { - this.uuid = value; - } - - /** - * Gets the value of the iconID property. - * - */ - public int getIconID() { - return iconID; - } - - /** - * Sets the value of the iconID property. - * - */ - public void setIconID(int value) { - this.iconID = value; - } - - /** - * Gets the value of the customIconUUID property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getCustomIconUUID() { - return customIconUUID; - } - - /** - * Sets the value of the customIconUUID property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setCustomIconUUID(UUID value) { - this.customIconUUID = value; - } - - /** - * Gets the value of the foregroundColor property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getForegroundColor() { - return foregroundColor; - } - - /** - * Sets the value of the foregroundColor property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setForegroundColor(String value) { - this.foregroundColor = value; - } - - /** - * Gets the value of the backgroundColor property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getBackgroundColor() { - return backgroundColor; - } - - /** - * Sets the value of the backgroundColor property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setBackgroundColor(String value) { - this.backgroundColor = value; - } - - /** - * Gets the value of the overrideURL property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getOverrideURL() { - return overrideURL; - } - - /** - * Sets the value of the overrideURL property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setOverrideURL(String value) { - this.overrideURL = value; - } - - /** - * Gets the value of the tags property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getTags() { - return tags; - } - - /** - * Sets the value of the tags property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setTags(String value) { - this.tags = value; - } - - /** - * Gets the value of the times property. - * - * @return - * possible object is - * {@link Times } - * - */ - public Times getTimes() { - return times; - } - - /** - * Sets the value of the times property. - * - * @param value - * allowed object is - * {@link Times } - * - */ - public void setTimes(Times value) { - this.times = value; - } - - /** - * Gets the value of the string property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the string property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getString().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link StringField } - * - * - */ - public List getString() { - if (string == null) { - string = new ArrayList(); - } - return this.string; - } - - /** - * Gets the value of the binary property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the binary property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getBinary().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link BinaryField } - * - * - */ - public List getBinary() { - if (binary == null) { - binary = new ArrayList(); - } - return this.binary; - } - - /** - * Gets the value of the autoType property. - * - * @return - * possible object is - * {@link AutoType } - * - */ - public AutoType getAutoType() { - return autoType; - } - - /** - * Sets the value of the autoType property. - * - * @param value - * allowed object is - * {@link AutoType } - * - */ - public void setAutoType(AutoType value) { - this.autoType = value; - } - - /** - * Gets the value of the history property. - * - * @return - * possible object is - * {@link History } - * - */ - public History getHistory() { - return history; - } - - /** - * Sets the value of the history property. - * - * @param value - * allowed object is - * {@link History } - * - */ - public void setHistory(History value) { - this.history = value; - } - - /** - * Gets the value of the customData property. - * - * @return - * possible object is - * {@link CustomData } - * - */ - public CustomData getCustomData() { - return customData; - } - - /** - * Sets the value of the customData property. - * - * @param value - * allowed object is - * {@link CustomData } - * - */ - public void setCustomData(CustomData value) { - this.customData = value; - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbGroupBinding.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbGroupBinding.java deleted file mode 100644 index 43c74da0..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/JaxbGroupBinding.java +++ /dev/null @@ -1,456 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.ArrayList; -import java.util.List; -import java.util.UUID; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlSchemaType; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.linguafranca.pwdb.kdbx.jaxb.base.AbstractJaxbParentedBinding; - - -/** - *

Java class for Group element declaration. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <element name="Group">
- *   <complexType>
- *     <complexContent>
- *       <extension base="{}Parented">
- *         <sequence>
- *           <element name="UUID" type="{}uuid"/>
- *           <element name="Name" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *           <element name="Notes" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *           <element name="IconID" type="{}iconId"/>
- *           <element name="CustomIconUUID" type="{}customIconUuidRef" minOccurs="0"/>
- *           <element ref="{}Times"/>
- *           <element name="IsExpanded" type="{}keepassBoolean"/>
- *           <element name="DefaultAutoTypeSequence" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *           <element name="EnableAutoType" type="{}keepassBoolean"/>
- *           <element name="EnableSearching" type="{}keepassBoolean"/>
- *           <element name="LastTopVisibleEntry" type="{}uuidRef"/>
- *           <element ref="{}Entry" maxOccurs="unbounded" minOccurs="0"/>
- *           <element ref="{}Group" maxOccurs="unbounded" minOccurs="0"/>
- *           <element name="CustomData" type="{}customData" minOccurs="0"/>
- *         </sequence>
- *       </extension>
- *     </complexContent>
- *   </complexType>
- * </element>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = { - "uuid", - "name", - "notes", - "iconID", - "customIconUUID", - "times", - "isExpanded", - "defaultAutoTypeSequence", - "enableAutoType", - "enableSearching", - "lastTopVisibleEntry", - "entry", - "group", - "customData" -}) -@XmlRootElement(name = "Group") -public class JaxbGroupBinding - extends AbstractJaxbParentedBinding -{ - - @XmlElement(name = "UUID", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID uuid; - @XmlElement(name = "Name", required = true) - protected String name; - @XmlElement(name = "Notes", required = true) - protected String notes; - @XmlElement(name = "IconID") - protected int iconID; - @XmlElement(name = "CustomIconUUID", type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID customIconUUID; - @XmlElement(name = "Times", required = true) - protected Times times; - @XmlElement(name = "IsExpanded", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean isExpanded; - @XmlElement(name = "DefaultAutoTypeSequence", required = true) - protected String defaultAutoTypeSequence; - @XmlElement(name = "EnableAutoType", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean enableAutoType; - @XmlElement(name = "EnableSearching", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean enableSearching; - @XmlElement(name = "LastTopVisibleEntry", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID lastTopVisibleEntry; - @XmlElement(name = "Entry") - protected List entry; - @XmlElement(name = "Group") - protected List group; - @XmlElement(name = "CustomData") - protected CustomData customData; - - /** - * Gets the value of the uuid property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getUUID() { - return uuid; - } - - /** - * Sets the value of the uuid property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setUUID(UUID value) { - this.uuid = value; - } - - /** - * Gets the value of the name property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getName() { - return name; - } - - /** - * Sets the value of the name property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setName(String value) { - this.name = value; - } - - /** - * Gets the value of the notes property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getNotes() { - return notes; - } - - /** - * Sets the value of the notes property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setNotes(String value) { - this.notes = value; - } - - /** - * Gets the value of the iconID property. - * - */ - public int getIconID() { - return iconID; - } - - /** - * Sets the value of the iconID property. - * - */ - public void setIconID(int value) { - this.iconID = value; - } - - /** - * Gets the value of the customIconUUID property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getCustomIconUUID() { - return customIconUUID; - } - - /** - * Sets the value of the customIconUUID property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setCustomIconUUID(UUID value) { - this.customIconUUID = value; - } - - /** - * Gets the value of the times property. - * - * @return - * possible object is - * {@link Times } - * - */ - public Times getTimes() { - return times; - } - - /** - * Sets the value of the times property. - * - * @param value - * allowed object is - * {@link Times } - * - */ - public void setTimes(Times value) { - this.times = value; - } - - /** - * Gets the value of the isExpanded property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getIsExpanded() { - return isExpanded; - } - - /** - * Sets the value of the isExpanded property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setIsExpanded(Boolean value) { - this.isExpanded = value; - } - - /** - * Gets the value of the defaultAutoTypeSequence property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getDefaultAutoTypeSequence() { - return defaultAutoTypeSequence; - } - - /** - * Sets the value of the defaultAutoTypeSequence property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDefaultAutoTypeSequence(String value) { - this.defaultAutoTypeSequence = value; - } - - /** - * Gets the value of the enableAutoType property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getEnableAutoType() { - return enableAutoType; - } - - /** - * Sets the value of the enableAutoType property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setEnableAutoType(Boolean value) { - this.enableAutoType = value; - } - - /** - * Gets the value of the enableSearching property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getEnableSearching() { - return enableSearching; - } - - /** - * Sets the value of the enableSearching property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setEnableSearching(Boolean value) { - this.enableSearching = value; - } - - /** - * Gets the value of the lastTopVisibleEntry property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getLastTopVisibleEntry() { - return lastTopVisibleEntry; - } - - /** - * Sets the value of the lastTopVisibleEntry property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setLastTopVisibleEntry(UUID value) { - this.lastTopVisibleEntry = value; - } - - /** - * Gets the value of the entry property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the entry property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getEntry().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link JaxbEntryBinding } - * - * - */ - public List getEntry() { - if (entry == null) { - entry = new ArrayList(); - } - return this.entry; - } - - /** - * Gets the value of the group property. - * - *

- * This accessor method returns a reference to the live list, - * not a snapshot. Therefore any modification you make to the - * returned list will be present inside the JAXB object. - * This is why there is not a set method for the group property. - * - *

- * For example, to add a new item, do as follows: - *

-     *    getGroup().add(newItem);
-     * 
- * - * - *

- * Objects of the following type(s) are allowed in the list - * {@link JaxbGroupBinding } - * - * - */ - public List getGroup() { - if (group == null) { - group = new ArrayList(); - } - return this.group; - } - - /** - * Gets the value of the customData property. - * - * @return - * possible object is - * {@link CustomData } - * - */ - public CustomData getCustomData() { - return customData; - } - - /** - * Sets the value of the customData property. - * - * @param value - * allowed object is - * {@link CustomData } - * - */ - public void setCustomData(CustomData value) { - this.customData = value; - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/KeePassFile.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/KeePassFile.java deleted file mode 100644 index 7b2ec424..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/KeePassFile.java +++ /dev/null @@ -1,1173 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.Date; -import java.util.UUID; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlSchemaType; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - - -/** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType>
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="Meta">
- *           <complexType>
- *             <complexContent>
- *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 <sequence>
- *                   <element name="Generator" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *                   <element name="HeaderHash" type="{http://www.w3.org/2001/XMLSchema}base64Binary" minOccurs="0"/>
- *                   <element name="DatabaseName" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *                   <element name="DatabaseNameChanged" type="{}keepassDateTime"/>
- *                   <element name="DatabaseDescription" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *                   <element name="DatabaseDescriptionChanged" type="{}keepassDateTime"/>
- *                   <element name="DefaultUserName" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *                   <element name="DefaultUserNameChanged" type="{}keepassDateTime"/>
- *                   <element name="MaintenanceHistoryDays" type="{http://www.w3.org/2001/XMLSchema}int"/>
- *                   <element name="Color" type="{}color"/>
- *                   <element name="MasterKeyChanged" type="{}keepassDateTime"/>
- *                   <element name="MasterKeyChangeRec" type="{}changeDays"/>
- *                   <element name="MasterKeyChangeForce" type="{}changeDays"/>
- *                   <element name="MemoryProtection">
- *                     <complexType>
- *                       <complexContent>
- *                         <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                           <sequence>
- *                             <element name="ProtectTitle" type="{}keepassBoolean"/>
- *                             <element name="ProtectUserName" type="{}keepassBoolean"/>
- *                             <element name="ProtectPassword" type="{}keepassBoolean"/>
- *                             <element name="ProtectURL" type="{}keepassBoolean"/>
- *                             <element name="ProtectNotes" type="{}keepassBoolean"/>
- *                           </sequence>
- *                         </restriction>
- *                       </complexContent>
- *                     </complexType>
- *                   </element>
- *                   <element name="CustomIcons" type="{}customIcons"/>
- *                   <element name="RecycleBinEnabled" type="{}keepassBoolean"/>
- *                   <element name="RecycleBinUUID" type="{}uuidRef"/>
- *                   <element name="RecycleBinChanged" type="{}keepassDateTime"/>
- *                   <element name="EntryTemplatesGroup" type="{}uuidRef"/>
- *                   <element name="EntryTemplatesGroupChanged" type="{}keepassDateTime"/>
- *                   <element name="LastSelectedGroup" type="{}uuidRef"/>
- *                   <element name="LastTopVisibleGroup" type="{}uuidRef"/>
- *                   <element name="HistoryMaxItems" type="{http://www.w3.org/2001/XMLSchema}int"/>
- *                   <element name="HistoryMaxSize" type="{http://www.w3.org/2001/XMLSchema}int"/>
- *                   <element name="Binaries" type="{}binaries" minOccurs="0"/>
- *                   <element name="CustomData" type="{}customData"/>
- *                 </sequence>
- *               </restriction>
- *             </complexContent>
- *           </complexType>
- *         </element>
- *         <element name="Root">
- *           <complexType>
- *             <complexContent>
- *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *                 <sequence>
- *                   <element ref="{}Group"/>
- *                   <element name="DeletedObjects" type="{http://www.w3.org/2001/XMLSchema}anyType"/>
- *                 </sequence>
- *               </restriction>
- *             </complexContent>
- *           </complexType>
- *         </element>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = { - "meta", - "root" -}) -@XmlRootElement(name = "KeePassFile") -public class KeePassFile { - - @XmlElement(name = "Meta", required = true) - protected KeePassFile.Meta meta; - @XmlElement(name = "Root", required = true) - protected KeePassFile.Root root; - - /** - * Gets the value of the meta property. - * - * @return - * possible object is - * {@link KeePassFile.Meta } - * - */ - public KeePassFile.Meta getMeta() { - return meta; - } - - /** - * Sets the value of the meta property. - * - * @param value - * allowed object is - * {@link KeePassFile.Meta } - * - */ - public void setMeta(KeePassFile.Meta value) { - this.meta = value; - } - - /** - * Gets the value of the root property. - * - * @return - * possible object is - * {@link KeePassFile.Root } - * - */ - public KeePassFile.Root getRoot() { - return root; - } - - /** - * Sets the value of the root property. - * - * @param value - * allowed object is - * {@link KeePassFile.Root } - * - */ - public void setRoot(KeePassFile.Root value) { - this.root = value; - } - - - /** - * This contains database settings and also contains information - * about the last state of the program that used the database. Presumably assuming - * that the same program will be used to open it again, or that a different program - * would want/need the same settings. - * - * - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-     * <complexType>
-     *   <complexContent>
-     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *       <sequence>
-     *         <element name="Generator" type="{http://www.w3.org/2001/XMLSchema}string"/>
-     *         <element name="HeaderHash" type="{http://www.w3.org/2001/XMLSchema}base64Binary" minOccurs="0"/>
-     *         <element name="DatabaseName" type="{http://www.w3.org/2001/XMLSchema}string"/>
-     *         <element name="DatabaseNameChanged" type="{}keepassDateTime"/>
-     *         <element name="DatabaseDescription" type="{http://www.w3.org/2001/XMLSchema}string"/>
-     *         <element name="DatabaseDescriptionChanged" type="{}keepassDateTime"/>
-     *         <element name="DefaultUserName" type="{http://www.w3.org/2001/XMLSchema}string"/>
-     *         <element name="DefaultUserNameChanged" type="{}keepassDateTime"/>
-     *         <element name="MaintenanceHistoryDays" type="{http://www.w3.org/2001/XMLSchema}int"/>
-     *         <element name="Color" type="{}color"/>
-     *         <element name="MasterKeyChanged" type="{}keepassDateTime"/>
-     *         <element name="MasterKeyChangeRec" type="{}changeDays"/>
-     *         <element name="MasterKeyChangeForce" type="{}changeDays"/>
-     *         <element name="MemoryProtection">
-     *           <complexType>
-     *             <complexContent>
-     *               <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *                 <sequence>
-     *                   <element name="ProtectTitle" type="{}keepassBoolean"/>
-     *                   <element name="ProtectUserName" type="{}keepassBoolean"/>
-     *                   <element name="ProtectPassword" type="{}keepassBoolean"/>
-     *                   <element name="ProtectURL" type="{}keepassBoolean"/>
-     *                   <element name="ProtectNotes" type="{}keepassBoolean"/>
-     *                 </sequence>
-     *               </restriction>
-     *             </complexContent>
-     *           </complexType>
-     *         </element>
-     *         <element name="CustomIcons" type="{}customIcons"/>
-     *         <element name="RecycleBinEnabled" type="{}keepassBoolean"/>
-     *         <element name="RecycleBinUUID" type="{}uuidRef"/>
-     *         <element name="RecycleBinChanged" type="{}keepassDateTime"/>
-     *         <element name="EntryTemplatesGroup" type="{}uuidRef"/>
-     *         <element name="EntryTemplatesGroupChanged" type="{}keepassDateTime"/>
-     *         <element name="LastSelectedGroup" type="{}uuidRef"/>
-     *         <element name="LastTopVisibleGroup" type="{}uuidRef"/>
-     *         <element name="HistoryMaxItems" type="{http://www.w3.org/2001/XMLSchema}int"/>
-     *         <element name="HistoryMaxSize" type="{http://www.w3.org/2001/XMLSchema}int"/>
-     *         <element name="Binaries" type="{}binaries" minOccurs="0"/>
-     *         <element name="CustomData" type="{}customData"/>
-     *       </sequence>
-     *     </restriction>
-     *   </complexContent>
-     * </complexType>
-     * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "generator", - "headerHash", - "databaseName", - "databaseNameChanged", - "databaseDescription", - "databaseDescriptionChanged", - "defaultUserName", - "defaultUserNameChanged", - "maintenanceHistoryDays", - "color", - "masterKeyChanged", - "masterKeyChangeRec", - "masterKeyChangeForce", - "memoryProtection", - "customIcons", - "recycleBinEnabled", - "recycleBinUUID", - "recycleBinChanged", - "entryTemplatesGroup", - "entryTemplatesGroupChanged", - "lastSelectedGroup", - "lastTopVisibleGroup", - "historyMaxItems", - "historyMaxSize", - "binaries", - "customData" - }) - public static class Meta { - - @XmlElement(name = "Generator", required = true) - protected String generator; - @XmlElement(name = "HeaderHash") - protected byte[] headerHash; - @XmlElement(name = "DatabaseName", required = true) - protected String databaseName; - @XmlElement(name = "DatabaseNameChanged", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date databaseNameChanged; - @XmlElement(name = "DatabaseDescription", required = true) - protected String databaseDescription; - @XmlElement(name = "DatabaseDescriptionChanged", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date databaseDescriptionChanged; - @XmlElement(name = "DefaultUserName", required = true) - protected String defaultUserName; - @XmlElement(name = "DefaultUserNameChanged", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date defaultUserNameChanged; - @XmlElement(name = "MaintenanceHistoryDays") - protected int maintenanceHistoryDays; - @XmlElement(name = "Color", required = true) - protected String color; - @XmlElement(name = "MasterKeyChanged", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date masterKeyChanged; - @XmlElement(name = "MasterKeyChangeRec") - protected int masterKeyChangeRec; - @XmlElement(name = "MasterKeyChangeForce") - protected int masterKeyChangeForce; - @XmlElement(name = "MemoryProtection", required = true) - protected KeePassFile.Meta.MemoryProtection memoryProtection; - @XmlElement(name = "CustomIcons", required = true) - protected CustomIcons customIcons; - @XmlElement(name = "RecycleBinEnabled", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean recycleBinEnabled; - @XmlElement(name = "RecycleBinUUID", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID recycleBinUUID; - @XmlElement(name = "RecycleBinChanged", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date recycleBinChanged; - @XmlElement(name = "EntryTemplatesGroup", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID entryTemplatesGroup; - @XmlElement(name = "EntryTemplatesGroupChanged", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date entryTemplatesGroupChanged; - @XmlElement(name = "LastSelectedGroup", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID lastSelectedGroup; - @XmlElement(name = "LastTopVisibleGroup", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter3 .class) - @XmlSchemaType(name = "base64Binary") - protected UUID lastTopVisibleGroup; - @XmlElement(name = "HistoryMaxItems") - protected int historyMaxItems; - @XmlElement(name = "HistoryMaxSize") - protected int historyMaxSize; - @XmlElement(name = "Binaries") - protected Binaries binaries; - @XmlElement(name = "CustomData", required = true) - protected CustomData customData; - - /** - * Gets the value of the generator property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getGenerator() { - return generator; - } - - /** - * Sets the value of the generator property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setGenerator(String value) { - this.generator = value; - } - - /** - * Gets the value of the headerHash property. - * - * @return - * possible object is - * byte[] - */ - public byte[] getHeaderHash() { - return headerHash; - } - - /** - * Sets the value of the headerHash property. - * - * @param value - * allowed object is - * byte[] - */ - public void setHeaderHash(byte[] value) { - this.headerHash = value; - } - - /** - * Gets the value of the databaseName property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getDatabaseName() { - return databaseName; - } - - /** - * Sets the value of the databaseName property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDatabaseName(String value) { - this.databaseName = value; - } - - /** - * Gets the value of the databaseNameChanged property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getDatabaseNameChanged() { - return databaseNameChanged; - } - - /** - * Sets the value of the databaseNameChanged property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDatabaseNameChanged(Date value) { - this.databaseNameChanged = value; - } - - /** - * Gets the value of the databaseDescription property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getDatabaseDescription() { - return databaseDescription; - } - - /** - * Sets the value of the databaseDescription property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDatabaseDescription(String value) { - this.databaseDescription = value; - } - - /** - * Gets the value of the databaseDescriptionChanged property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getDatabaseDescriptionChanged() { - return databaseDescriptionChanged; - } - - /** - * Sets the value of the databaseDescriptionChanged property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDatabaseDescriptionChanged(Date value) { - this.databaseDescriptionChanged = value; - } - - /** - * Gets the value of the defaultUserName property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getDefaultUserName() { - return defaultUserName; - } - - /** - * Sets the value of the defaultUserName property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDefaultUserName(String value) { - this.defaultUserName = value; - } - - /** - * Gets the value of the defaultUserNameChanged property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getDefaultUserNameChanged() { - return defaultUserNameChanged; - } - - /** - * Sets the value of the defaultUserNameChanged property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setDefaultUserNameChanged(Date value) { - this.defaultUserNameChanged = value; - } - - /** - * Gets the value of the maintenanceHistoryDays property. - * - */ - public int getMaintenanceHistoryDays() { - return maintenanceHistoryDays; - } - - /** - * Sets the value of the maintenanceHistoryDays property. - * - */ - public void setMaintenanceHistoryDays(int value) { - this.maintenanceHistoryDays = value; - } - - /** - * Gets the value of the color property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getColor() { - return color; - } - - /** - * Sets the value of the color property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setColor(String value) { - this.color = value; - } - - /** - * Gets the value of the masterKeyChanged property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getMasterKeyChanged() { - return masterKeyChanged; - } - - /** - * Sets the value of the masterKeyChanged property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setMasterKeyChanged(Date value) { - this.masterKeyChanged = value; - } - - /** - * Gets the value of the masterKeyChangeRec property. - * - */ - public int getMasterKeyChangeRec() { - return masterKeyChangeRec; - } - - /** - * Sets the value of the masterKeyChangeRec property. - * - */ - public void setMasterKeyChangeRec(int value) { - this.masterKeyChangeRec = value; - } - - /** - * Gets the value of the masterKeyChangeForce property. - * - */ - public int getMasterKeyChangeForce() { - return masterKeyChangeForce; - } - - /** - * Sets the value of the masterKeyChangeForce property. - * - */ - public void setMasterKeyChangeForce(int value) { - this.masterKeyChangeForce = value; - } - - /** - * Gets the value of the memoryProtection property. - * - * @return - * possible object is - * {@link KeePassFile.Meta.MemoryProtection } - * - */ - public KeePassFile.Meta.MemoryProtection getMemoryProtection() { - return memoryProtection; - } - - /** - * Sets the value of the memoryProtection property. - * - * @param value - * allowed object is - * {@link KeePassFile.Meta.MemoryProtection } - * - */ - public void setMemoryProtection(KeePassFile.Meta.MemoryProtection value) { - this.memoryProtection = value; - } - - /** - * Gets the value of the customIcons property. - * - * @return - * possible object is - * {@link CustomIcons } - * - */ - public CustomIcons getCustomIcons() { - return customIcons; - } - - /** - * Sets the value of the customIcons property. - * - * @param value - * allowed object is - * {@link CustomIcons } - * - */ - public void setCustomIcons(CustomIcons value) { - this.customIcons = value; - } - - /** - * Gets the value of the recycleBinEnabled property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getRecycleBinEnabled() { - return recycleBinEnabled; - } - - /** - * Sets the value of the recycleBinEnabled property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setRecycleBinEnabled(Boolean value) { - this.recycleBinEnabled = value; - } - - /** - * Gets the value of the recycleBinUUID property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getRecycleBinUUID() { - return recycleBinUUID; - } - - /** - * Sets the value of the recycleBinUUID property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setRecycleBinUUID(UUID value) { - this.recycleBinUUID = value; - } - - /** - * Gets the value of the recycleBinChanged property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getRecycleBinChanged() { - return recycleBinChanged; - } - - /** - * Sets the value of the recycleBinChanged property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setRecycleBinChanged(Date value) { - this.recycleBinChanged = value; - } - - /** - * Gets the value of the entryTemplatesGroup property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getEntryTemplatesGroup() { - return entryTemplatesGroup; - } - - /** - * Sets the value of the entryTemplatesGroup property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setEntryTemplatesGroup(UUID value) { - this.entryTemplatesGroup = value; - } - - /** - * Gets the value of the entryTemplatesGroupChanged property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getEntryTemplatesGroupChanged() { - return entryTemplatesGroupChanged; - } - - /** - * Sets the value of the entryTemplatesGroupChanged property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setEntryTemplatesGroupChanged(Date value) { - this.entryTemplatesGroupChanged = value; - } - - /** - * Gets the value of the lastSelectedGroup property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getLastSelectedGroup() { - return lastSelectedGroup; - } - - /** - * Sets the value of the lastSelectedGroup property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setLastSelectedGroup(UUID value) { - this.lastSelectedGroup = value; - } - - /** - * Gets the value of the lastTopVisibleGroup property. - * - * @return - * possible object is - * {@link String } - * - */ - public UUID getLastTopVisibleGroup() { - return lastTopVisibleGroup; - } - - /** - * Sets the value of the lastTopVisibleGroup property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setLastTopVisibleGroup(UUID value) { - this.lastTopVisibleGroup = value; - } - - /** - * Gets the value of the historyMaxItems property. - * - */ - public int getHistoryMaxItems() { - return historyMaxItems; - } - - /** - * Sets the value of the historyMaxItems property. - * - */ - public void setHistoryMaxItems(int value) { - this.historyMaxItems = value; - } - - /** - * Gets the value of the historyMaxSize property. - * - */ - public int getHistoryMaxSize() { - return historyMaxSize; - } - - /** - * Sets the value of the historyMaxSize property. - * - */ - public void setHistoryMaxSize(int value) { - this.historyMaxSize = value; - } - - /** - * Gets the value of the binaries property. - * - * @return - * possible object is - * {@link Binaries } - * - */ - public Binaries getBinaries() { - return binaries; - } - - /** - * Sets the value of the binaries property. - * - * @param value - * allowed object is - * {@link Binaries } - * - */ - public void setBinaries(Binaries value) { - this.binaries = value; - } - - /** - * Gets the value of the customData property. - * - * @return - * possible object is - * {@link CustomData } - * - */ - public CustomData getCustomData() { - return customData; - } - - /** - * Sets the value of the customData property. - * - * @param value - * allowed object is - * {@link CustomData } - * - */ - public void setCustomData(CustomData value) { - this.customData = value; - } - - - /** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-         * <complexType>
-         *   <complexContent>
-         *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-         *       <sequence>
-         *         <element name="ProtectTitle" type="{}keepassBoolean"/>
-         *         <element name="ProtectUserName" type="{}keepassBoolean"/>
-         *         <element name="ProtectPassword" type="{}keepassBoolean"/>
-         *         <element name="ProtectURL" type="{}keepassBoolean"/>
-         *         <element name="ProtectNotes" type="{}keepassBoolean"/>
-         *       </sequence>
-         *     </restriction>
-         *   </complexContent>
-         * </complexType>
-         * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "protectTitle", - "protectUserName", - "protectPassword", - "protectURL", - "protectNotes" - }) - public static class MemoryProtection { - - @XmlElement(name = "ProtectTitle", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean protectTitle; - @XmlElement(name = "ProtectUserName", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean protectUserName; - @XmlElement(name = "ProtectPassword", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean protectPassword; - @XmlElement(name = "ProtectURL", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean protectURL; - @XmlElement(name = "ProtectNotes", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean protectNotes; - - /** - * Gets the value of the protectTitle property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getProtectTitle() { - return protectTitle; - } - - /** - * Sets the value of the protectTitle property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setProtectTitle(Boolean value) { - this.protectTitle = value; - } - - /** - * Gets the value of the protectUserName property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getProtectUserName() { - return protectUserName; - } - - /** - * Sets the value of the protectUserName property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setProtectUserName(Boolean value) { - this.protectUserName = value; - } - - /** - * Gets the value of the protectPassword property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getProtectPassword() { - return protectPassword; - } - - /** - * Sets the value of the protectPassword property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setProtectPassword(Boolean value) { - this.protectPassword = value; - } - - /** - * Gets the value of the protectURL property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getProtectURL() { - return protectURL; - } - - /** - * Sets the value of the protectURL property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setProtectURL(Boolean value) { - this.protectURL = value; - } - - /** - * Gets the value of the protectNotes property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getProtectNotes() { - return protectNotes; - } - - /** - * Sets the value of the protectNotes property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setProtectNotes(Boolean value) { - this.protectNotes = value; - } - - } - - } - - - /** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-     * <complexType>
-     *   <complexContent>
-     *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
-     *       <sequence>
-     *         <element ref="{}Group"/>
-     *         <element name="DeletedObjects" type="{http://www.w3.org/2001/XMLSchema}anyType"/>
-     *       </sequence>
-     *     </restriction>
-     *   </complexContent>
-     * </complexType>
-     * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "", propOrder = { - "group", - "deletedObjects" - }) - public static class Root { - - @XmlElement(name = "Group", required = true) - protected JaxbGroupBinding group; - @XmlElement(name = "DeletedObjects", required = true) - protected Object deletedObjects; - - /** - * This is the root group which contains everything. - * - * - * @return - * possible object is - * {@link JaxbGroupBinding } - * - */ - public JaxbGroupBinding getGroup() { - return group; - } - - /** - * Sets the value of the group property. - * - * @param value - * allowed object is - * {@link JaxbGroupBinding } - * - */ - public void setGroup(JaxbGroupBinding value) { - this.group = value; - } - - /** - * Gets the value of the deletedObjects property. - * - * @return - * possible object is - * {@link Object } - * - */ - public Object getDeletedObjects() { - return deletedObjects; - } - - /** - * Sets the value of the deletedObjects property. - * - * @param value - * allowed object is - * {@link Object } - * - */ - public void setDeletedObjects(Object value) { - this.deletedObjects = value; - } - - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ObjectFactory.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ObjectFactory.java deleted file mode 100644 index 0c3cf1e2..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ObjectFactory.java +++ /dev/null @@ -1,214 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import javax.xml.bind.JAXBElement; -import javax.xml.bind.annotation.XmlElementDecl; -import javax.xml.bind.annotation.XmlRegistry; -import javax.xml.namespace.QName; - - -/** - * This object contains factory methods for each - * Java content interface and Java element interface - * generated in the org.linguafranca.pwdb.kdbx.jaxb.binding package. - *

An ObjectFactory allows you to programatically - * construct new instances of the Java representation - * for XML content. The Java representation of XML - * content can consist of schema derived interfaces - * and classes representing the binding of schema - * type definitions, element declarations and model - * groups. Factory methods for each of these are - * provided in this class. - * - */ -@XmlRegistry -public class ObjectFactory { - - private final static QName _AutoTypeAssociationWindow_QNAME = new QName("", "Window"); - private final static QName _AutoTypeAssociationKeystrokeSequence_QNAME = new QName("", "KeystrokeSequence"); - - /** - * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: org.linguafranca.pwdb.kdbx.jaxb.binding - * - */ - public ObjectFactory() { - } - - /** - * Create an instance of {@link KeePassFile } - * - */ - public KeePassFile createKeePassFile() { - return new KeePassFile(); - } - - /** - * Create an instance of {@link Binaries } - * - */ - public Binaries createBinaries() { - return new Binaries(); - } - - /** - * Create an instance of {@link StringField } - * - */ - public StringField createStringField() { - return new StringField(); - } - - /** - * Create an instance of {@link AutoType } - * - */ - public AutoType createAutoType() { - return new AutoType(); - } - - /** - * Create an instance of {@link BinaryField } - * - */ - public BinaryField createBinaryField() { - return new BinaryField(); - } - - /** - * Create an instance of {@link CustomIcons } - * - */ - public CustomIcons createCustomIcons() { - return new CustomIcons(); - } - - /** - * Create an instance of {@link KeePassFile.Meta } - * - */ - public KeePassFile.Meta createKeePassFileMeta() { - return new KeePassFile.Meta(); - } - - /** - * Create an instance of {@link JaxbGroupBinding } - * - */ - public JaxbGroupBinding createJaxbGroupBinding() { - return new JaxbGroupBinding(); - } - - /** - * Create an instance of {@link JaxbEntryBinding } - * - */ - public JaxbEntryBinding createJaxbEntryBinding() { - return new JaxbEntryBinding(); - } - - /** - * Create an instance of {@link Times } - * - */ - public Times createTimes() { - return new Times(); - } - - /** - * Create an instance of {@link History } - * - */ - public History createHistory() { - return new History(); - } - - /** - * Create an instance of {@link KeePassFile.Root } - * - */ - public KeePassFile.Root createKeePassFileRoot() { - return new KeePassFile.Root(); - } - - /** - * Create an instance of {@link CustomData } - * - */ - public CustomData createCustomData() { - return new CustomData(); - } - - /** - * Create an instance of {@link Binaries.Binary } - * - */ - public Binaries.Binary createBinariesBinary() { - return new Binaries.Binary(); - } - - /** - * Create an instance of {@link StringField.Value } - * - */ - public StringField.Value createStringFieldValue() { - return new StringField.Value(); - } - - /** - * Create an instance of {@link AutoType.Association } - * - */ - public AutoType.Association createAutoTypeAssociation() { - return new AutoType.Association(); - } - - /** - * Create an instance of {@link BinaryField.Value } - * - */ - public BinaryField.Value createBinaryFieldValue() { - return new BinaryField.Value(); - } - - /** - * Create an instance of {@link CustomIcons.Icon } - * - */ - public CustomIcons.Icon createCustomIconsIcon() { - return new CustomIcons.Icon(); - } - - /** - * Create an instance of {@link KeePassFile.Meta.MemoryProtection } - * - */ - public KeePassFile.Meta.MemoryProtection createKeePassFileMetaMemoryProtection() { - return new KeePassFile.Meta.MemoryProtection(); - } - - /** - * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}} - * - */ - @XmlElementDecl(namespace = "", name = "Window", scope = AutoType.Association.class) - public JAXBElement createAutoTypeAssociationWindow(String value) { - return new JAXBElement(_AutoTypeAssociationWindow_QNAME, String.class, AutoType.Association.class, value); - } - - /** - * Create an instance of {@link JAXBElement }{@code <}{@link String }{@code >}} - * - */ - @XmlElementDecl(namespace = "", name = "KeystrokeSequence", scope = AutoType.Association.class) - public JAXBElement createAutoTypeAssociationKeystrokeSequence(String value) { - return new JAXBElement(_AutoTypeAssociationKeystrokeSequence_QNAME, String.class, AutoType.Association.class, value); - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Parented.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Parented.java deleted file mode 100644 index 331b6222..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Parented.java +++ /dev/null @@ -1,34 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlSeeAlso; -import javax.xml.bind.annotation.XmlType; - - -/** - * This class, while generated, is not used see {@link - * org.linguafranca.pwdb.kdbx.jaxb.org.linguafranca.pwdb.base.AbstractJaxbParentedBinding} which is the - * actual implementation class. - * - * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "Parented") -@XmlSeeAlso({ - JaxbGroupBinding.class, - JaxbEntryBinding.class -}) -public abstract class Parented { - - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/StringField.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/StringField.java deleted file mode 100644 index 887ec91e..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/StringField.java +++ /dev/null @@ -1,205 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlAttribute; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; -import org.linguafranca.pwdb.kdbx.jaxb.base.ValueBinding; - - -/** - * This is where the values of the database are actually stored. You can have String valued - * fields, and you can have Binary valued fields. There are "Default" String fields (username and so on) and - * there are custom string fields (custom only in that their names are not the names of default string - * fields). Not really clear whether the keys are case-sensitive. - * - * - *

Java class for stringField complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="stringField">
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="Key" type="{http://www.w3.org/2001/XMLSchema}string"/>
- *         <element name="Value">
- *           <complexType>
- *             <simpleContent>
- *               <extension base="<>ValueExtender">
- *                 <attribute name="Protected" type="{}keepassBoolean" default="False" />
- *                 <attribute name="ProtectInMemory" type="{}keepassBoolean" default="False" />
- *               </extension>
- *             </simpleContent>
- *           </complexType>
- *         </element>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "stringField", propOrder = { - "key", - "value" -}) -public class StringField { - - @XmlElement(name = "Key", required = true) - protected String key; - @XmlElement(name = "Value", required = true) - protected StringField.Value value; - - /** - * Gets the value of the key property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getKey() { - return key; - } - - /** - * Sets the value of the key property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setKey(String value) { - this.key = value; - } - - /** - * Gets the value of the value property. - * - * @return - * possible object is - * {@link StringField.Value } - * - */ - public StringField.Value getValue() { - return value; - } - - /** - * Sets the value of the value property. - * - * @param value - * allowed object is - * {@link StringField.Value } - * - */ - public void setValue(StringField.Value value) { - this.value = value; - } - - - /** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

-     * <complexType>
-     *   <simpleContent>
-     *     <extension base="<>ValueExtender">
-     *       <attribute name="Protected" type="{}keepassBoolean" default="False" />
-     *       <attribute name="ProtectInMemory" type="{}keepassBoolean" default="False" />
-     *     </extension>
-     *   </simpleContent>
-     * </complexType>
-     * 
- * - * - */ - @XmlAccessorType(XmlAccessType.FIELD) - @XmlType(name = "") - public static class Value - extends ValueBinding - { - - @XmlAttribute(name = "Protected") - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean _protected; - @XmlAttribute(name = "ProtectInMemory") - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean protectInMemory; - - /** - * Gets the value of the protected property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getProtected() { - if (_protected == null) { - return new Adapter2().unmarshal("False"); - } else { - return _protected; - } - } - - /** - * Sets the value of the protected property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setProtected(Boolean value) { - this._protected = value; - } - - /** - * Gets the value of the protectInMemory property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getProtectInMemory() { - if (protectInMemory == null) { - return new Adapter2().unmarshal("False"); - } else { - return protectInMemory; - } - } - - /** - * Sets the value of the protectInMemory property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setProtectInMemory(Boolean value) { - this.protectInMemory = value; - } - - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Times.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Times.java deleted file mode 100644 index 2d371477..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/Times.java +++ /dev/null @@ -1,245 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import java.util.Date; -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.bind.annotation.XmlRootElement; -import javax.xml.bind.annotation.XmlSchemaType; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; - - -/** - *

Java class for anonymous complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType>
- *   <complexContent>
- *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
- *       <sequence>
- *         <element name="LastModificationTime" type="{}keepassDateTime"/>
- *         <element name="CreationTime" type="{}keepassDateTime"/>
- *         <element name="LastAccessTime" type="{}keepassDateTime"/>
- *         <element name="ExpiryTime" type="{}keepassDateTime"/>
- *         <element name="Expires" type="{}keepassBoolean"/>
- *         <element name="UsageCount" type="{http://www.w3.org/2001/XMLSchema}int"/>
- *         <element name="LocationChanged" type="{}keepassDateTime"/>
- *       </sequence>
- *     </restriction>
- *   </complexContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "", propOrder = { - "lastModificationTime", - "creationTime", - "lastAccessTime", - "expiryTime", - "expires", - "usageCount", - "locationChanged" -}) -@XmlRootElement(name = "Times") -public class Times { - - @XmlElement(name = "LastModificationTime", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date lastModificationTime; - @XmlElement(name = "CreationTime", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date creationTime; - @XmlElement(name = "LastAccessTime", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date lastAccessTime; - @XmlElement(name = "ExpiryTime", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date expiryTime; - @XmlElement(name = "Expires", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter2 .class) - protected Boolean expires; - @XmlElement(name = "UsageCount") - protected int usageCount; - @XmlElement(name = "LocationChanged", required = true, type = String.class) - @XmlJavaTypeAdapter(Adapter1 .class) - @XmlSchemaType(name = "dateTime") - protected Date locationChanged; - - /** - * Gets the value of the lastModificationTime property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getLastModificationTime() { - return lastModificationTime; - } - - /** - * Sets the value of the lastModificationTime property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setLastModificationTime(Date value) { - this.lastModificationTime = value; - } - - /** - * Gets the value of the creationTime property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getCreationTime() { - return creationTime; - } - - /** - * Sets the value of the creationTime property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setCreationTime(Date value) { - this.creationTime = value; - } - - /** - * Gets the value of the lastAccessTime property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getLastAccessTime() { - return lastAccessTime; - } - - /** - * Sets the value of the lastAccessTime property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setLastAccessTime(Date value) { - this.lastAccessTime = value; - } - - /** - * Gets the value of the expiryTime property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getExpiryTime() { - return expiryTime; - } - - /** - * Sets the value of the expiryTime property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setExpiryTime(Date value) { - this.expiryTime = value; - } - - /** - * Gets the value of the expires property. - * - * @return - * possible object is - * {@link String } - * - */ - public Boolean getExpires() { - return expires; - } - - /** - * Sets the value of the expires property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setExpires(Boolean value) { - this.expires = value; - } - - /** - * Gets the value of the usageCount property. - * - */ - public int getUsageCount() { - return usageCount; - } - - /** - * Sets the value of the usageCount property. - * - */ - public void setUsageCount(int value) { - this.usageCount = value; - } - - /** - * Gets the value of the locationChanged property. - * - * @return - * possible object is - * {@link String } - * - */ - public Date getLocationChanged() { - return locationChanged; - } - - /** - * Sets the value of the locationChanged property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setLocationChanged(Date value) { - this.locationChanged = value; - } - -} diff --git a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ValueExtender.java b/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ValueExtender.java deleted file mode 100644 index df8407c8..00000000 --- a/jaxb/src/generated/java/org/linguafranca/pwdb/kdbx/jaxb/binding/ValueExtender.java +++ /dev/null @@ -1,75 +0,0 @@ -// -// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 -// See http://java.sun.com/xml/jaxb -// Any modifications to this file will be lost upon recompilation of the source schema. -// Generated on: 2023.05.17 at 11:23:19 AM BST -// - - -package org.linguafranca.pwdb.kdbx.jaxb.binding; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlSeeAlso; -import javax.xml.bind.annotation.XmlType; -import javax.xml.bind.annotation.XmlValue; - - -/** - * Abstract parent for StringField/Value so that - * to allow creation of a custom mapping that contains extra - * non-serialised (transient) fields. - * - * - *

Java class for ValueExtender complex type. - * - *

The following schema fragment specifies the expected content contained within this class. - * - *

- * <complexType name="ValueExtender">
- *   <simpleContent>
- *     <extension base="<http://www.w3.org/2001/XMLSchema>string">
- *     </extension>
- *   </simpleContent>
- * </complexType>
- * 
- * - * - */ -@XmlAccessorType(XmlAccessType.FIELD) -@XmlType(name = "ValueExtender", propOrder = { - "value" -}) -@XmlSeeAlso({ - org.linguafranca.pwdb.kdbx.jaxb.binding.StringField.Value.class -}) -public abstract class ValueExtender { - - @XmlValue - protected String value; - - /** - * Gets the value of the value property. - * - * @return - * possible object is - * {@link String } - * - */ - public String getValue() { - return value; - } - - /** - * Sets the value of the value property. - * - * @param value - * allowed object is - * {@link String } - * - */ - public void setValue(String value) { - this.value = value; - } - -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbDatabase.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbDatabase.java deleted file mode 100644 index 1059d52f..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbDatabase.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.StreamConfiguration; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.base.AbstractDatabase; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; -import org.linguafranca.pwdb.kdbx.jaxb.binding.KeePassFile; -import org.linguafranca.pwdb.kdbx.jaxb.binding.ObjectFactory; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Date; -import java.util.Objects; -import java.util.UUID; - -/** - * Implementation of {@link org.linguafranca.pwdb.Database} for JAXB. - * - * @author jo - */ - -public class JaxbDatabase extends AbstractDatabase { - - private final KeePassFile keePassFile; - private final ObjectFactory objectFactory = new ObjectFactory(); - private final JaxbGroup root; - private StreamFormat streamFormat; - - public JaxbDatabase() { - this(createEmptyDatabase().getKeePassFile(), null); - } - - private JaxbDatabase(KeePassFile keePassFile, StreamFormat streamFormat) { - this.keePassFile = keePassFile; - this.root = new JaxbGroup(this, keePassFile.getRoot().getGroup()); - this.streamFormat = streamFormat; - } - - public static JaxbDatabase createEmptyDatabase() { - InputStream inputStream = JaxbDatabase.class.getClassLoader().getResourceAsStream("base.kdbx.xml"); - KeePassFile keePassFile = new JaxbSerializableDatabase().load(inputStream).keePassFile; - keePassFile.getRoot().getGroup().setUUID(UUID.randomUUID()); - return new JaxbDatabase(keePassFile, null); - } - - public static JaxbDatabase load(Credentials creds, InputStream inputStream) throws IOException { - return load(new KdbxStreamFormat(), creds, inputStream); - } - - @NotNull - public static JaxbDatabase load(StreamFormat format, Credentials creds, InputStream inputStream) throws IOException { - JaxbSerializableDatabase db = new JaxbSerializableDatabase(); - format.load(db, creds, inputStream); - return new JaxbDatabase(db.getKeePassFile(), format); - } - - @Override - public void save(Credentials creds, OutputStream outputStream) throws IOException { - if (Objects.isNull(this.streamFormat)){ - this.streamFormat = new KdbxStreamFormat(new KdbxHeader(4)); - } - save(this.streamFormat, creds, outputStream); - } - - public void save(StreamFormat format, Credentials creds, OutputStream outputStream) throws IOException { - keePassFile.getMeta().setGenerator("KeePassJava2-JAXB"); - JaxbSerializableDatabase jsd = new JaxbSerializableDatabase(this.keePassFile); - format.save(jsd, creds, outputStream); - setDirty(false); - } - - @Override - public JaxbGroup getRootGroup() { - return root; - } - - @Override - public JaxbGroup getRecycleBin() { - UUID recycleBinUuid = this.keePassFile.getMeta().getRecycleBinUUID(); - JaxbGroup g = findGroup(recycleBinUuid); - if (g == null && !isRecycleBinEnabled()) { - return null; - } - if (g == null) { - g = newGroup("Recycle Bin"); - getRootGroup().addGroup(g); - this.keePassFile.getMeta().setRecycleBinUUID(g.getUuid()); - this.keePassFile.getMeta().setRecycleBinChanged(new Date()); - } - return g; - } - - @Override - public boolean isRecycleBinEnabled() { - return this.keePassFile.getMeta().getRecycleBinEnabled(); - } - - @Override - public void enableRecycleBin(boolean enable) { - this.keePassFile.getMeta().setRecycleBinEnabled(enable); - } - - @Override - public JaxbGroup newGroup() { - return new JaxbGroup(this); - } - - @Override - public JaxbEntry newEntry() { - return new JaxbEntry(this); - } - - @Override - public JaxbIcon newIcon() { - return new JaxbIcon(); - } - - @Override - public JaxbIcon newIcon(Integer i) { - return new JaxbIcon(i); - } - - @Override - public String getDescription() { - return keePassFile.getMeta().getDatabaseDescription(); - } - - @Override - public void setDescription(String description) { - keePassFile.getMeta().setDatabaseDescription(description); - } - - @Override - public boolean shouldProtect(String propertyName) { - switch (propertyName.toLowerCase()) { - case "title": - return keePassFile.getMeta().getMemoryProtection().getProtectTitle(); - case "username": - return keePassFile.getMeta().getMemoryProtection().getProtectUserName(); - case "password": - return keePassFile.getMeta().getMemoryProtection().getProtectPassword(); - case "url": - return keePassFile.getMeta().getMemoryProtection().getProtectURL(); - case "notes": - return keePassFile.getMeta().getMemoryProtection().getProtectNotes(); - default: - return false; - } - } - - @Override - public String getName() { - return keePassFile.getMeta().getDatabaseName(); - } - - @Override - public void setName(String s) { - keePassFile.getMeta().setDatabaseName(s); - keePassFile.getMeta().setDatabaseNameChanged(new Date()); - } - - public KeePassFile getKeePassFile() { - return keePassFile; - } - - ObjectFactory getObjectFactory() { - return objectFactory; - } - - public void createBinary(byte[] value, Integer index) { - JaxbSerializableDatabase.addBinary(getKeePassFile(), getObjectFactory(), index, value); - } - - @SuppressWarnings("unchecked") - @Override - public StreamFormat getStreamFormat(){ - return streamFormat; - } -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbEntry.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbEntry.java deleted file mode 100644 index 1d4bea59..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbEntry.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.base.AbstractEntry; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.jaxb.binding.*; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -/** - * Implementation of {@link org.linguafranca.pwdb.Entry} for JAXB. - * - *

The class wraps an underlying JAXB generated delegate. - * - * @author jo - */ -@SuppressWarnings("WeakerAccess") -public class JaxbEntry extends AbstractEntry { - - protected JaxbDatabase database; - protected JaxbEntryBinding delegate; - - public JaxbEntry(JaxbDatabase jaxbDatabase) { - this.database = jaxbDatabase; - this.delegate = new JaxbEntryBinding(); - - for (String s: STANDARD_PROPERTY_NAMES) { - StringField field = jaxbDatabase.getObjectFactory().createStringField(); - field.setKey(s); - StringField.Value value = jaxbDatabase.getObjectFactory().createStringFieldValue(); - value.setValue(""); - field.setValue(value); - delegate.getString().add(field); - } - - Date now = new Date(System.currentTimeMillis() / 1000L * 1000L); // to nearest lower second - Times times = new Times(); - times.setLastModificationTime(now); - times.setCreationTime(now); - times.setLastAccessTime(now); - times.setExpiryTime(now); - times.setExpires(false); - times.setUsageCount(0); - times.setLocationChanged(now); - this.delegate.setTimes(times); - - delegate.setUUID(UUID.randomUUID()); - } - - public JaxbEntry(JaxbDatabase database, JaxbEntryBinding entry) { - this.database = database; - this.delegate = entry; - } - - @Override - public String getProperty(String name) { - for (StringField field: delegate.getString()){ - if (field.getKey().equals(name)){ - return field.getValue().getValue(); - } - } - return null; - } - - @Override - public void setPropertyValue(String name, String value) { - StringField toRemove = null; - for (StringField field: delegate.getString()){ - if (field.getKey().equals(name)) { - toRemove = field; - break; - } - } - if (toRemove != null) { - delegate.getString().remove(toRemove); - } - - StringField.Value fieldValue = database.getObjectFactory().createStringFieldValue(); - fieldValue.setValue(value); - fieldValue.setProtected(false); - StringField field = database.getObjectFactory().createStringField(); - field.setKey(name); - field.setValue(fieldValue); - delegate.getString().add(field); - touch(); - } - - @Override - public boolean removeProperty(String name) throws IllegalArgumentException { - if (STANDARD_PROPERTY_NAMES.contains(name)) throw new IllegalArgumentException("may not remove property: " + name); - - StringField toRemove = null; - for (StringField field: delegate.getString()){ - if (field.getKey().equals(name)) { - toRemove = field; - break; - } - } - if (toRemove == null) { - return false; - } else { - delegate.getString().remove(toRemove); - touch(); - return true; - } - } - - @Override - public List getPropertyNames() { - List result = new ArrayList<>(); - for (StringField stringField : delegate.getString()) { - result.add(stringField.getKey()); - } - return result; - } - - @Override - public byte[] getBinaryProperty(String name) { - for (BinaryField binaryField : delegate.getBinary()){ - if (binaryField.getKey().equals(name)){ - Integer ref = binaryField.getValue().getRef(); - for (Binaries.Binary binary: database.getKeePassFile().getMeta().getBinaries().getBinary()){ - if (binary.getID().equals(ref)) { - if (binary.getCompressed()) { - return Helpers.unzipBinaryContent(binary.getValue()); - } - return binary.getValue(); - } - } - } - } - return null; - } - - @Override - public void setBinaryProperty(String name, byte[] value) { - // remove old binary property with same name - BinaryField toRemove = null; - for (BinaryField binaryField : delegate.getBinary()) { - if (binaryField.getKey().equals(name)) { - toRemove = binaryField; - break; - } - } - if (toRemove != null) { - delegate.getBinary().remove(toRemove); - } - - // what is the next free index in the binary store? - Integer max = -1; - List binaryList = database.getKeePassFile().getMeta().getBinaries().getBinary(); - for (Binaries.Binary binary: binaryList){ - if (binary.getID() > max) { - max = binary.getID(); - } - } - max++; - - database.createBinary(value, max); - - // make a reference to it from the entry - BinaryField binaryField = database.getObjectFactory().createBinaryField(); - binaryField.setKey(name); - BinaryField.Value fieldValue = database.getObjectFactory().createBinaryFieldValue(); - fieldValue.setRef(max); - binaryField.setValue(fieldValue); - delegate.getBinary().add(binaryField); - touch(); - } - - @Override - public boolean removeBinaryProperty(String name) throws UnsupportedOperationException { - BinaryField toRemove = null; - for (BinaryField binaryField : delegate.getBinary()) { - if (binaryField.getKey().equals(name)) { - toRemove = binaryField; - break; - } - } - - if (toRemove == null) { - return false; - } else { - delegate.getBinary().remove(toRemove); - touch(); - return true; - } - } - - @Override - public List getBinaryPropertyNames() { - List result = new ArrayList<>(); - for (BinaryField binaryField : delegate.getBinary()) { - result.add(binaryField.getKey()); - } - return result; - } - - @Override - public JaxbGroup getParent() { - if (delegate.parent == null) { - return null; - } - return new JaxbGroup(database, ((JaxbGroupBinding) delegate.parent)); - } - - @Override - public @NotNull UUID getUuid() { - return delegate.getUUID(); - } - - @Override - public JaxbIcon getIcon() { - return new JaxbIcon(delegate.getIconID()); - } - - @Override - public void setIcon(JaxbIcon icon) { - delegate.setIconID(icon.getIndex()); - touch(); - } - - @Override - public Date getLastAccessTime() { - return delegate.getTimes().getLastAccessTime(); - } - - @Override - public Date getCreationTime() { - return delegate.getTimes().getCreationTime(); - } - - @Override - public boolean getExpires() { - return delegate.getTimes().getExpires(); - } - - @Override - public void setExpires(boolean expires) { - delegate.getTimes().setExpires(true); - } - - @Override - public Date getExpiryTime() { - return delegate.getTimes().getExpiryTime(); - } - - @Override - public void setExpiryTime(Date expiryTime) throws IllegalArgumentException { - if (expiryTime == null) throw new IllegalArgumentException("expiryTime may not be null"); - delegate.getTimes().setExpiryTime(expiryTime); - } - - @Override - public Date getLastModificationTime() { - return delegate.getTimes().getLastModificationTime(); - } - - @Override - protected void touch() { - database.setDirty(true); - delegate.getTimes().setLastModificationTime(new Date()); - } -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbGroup.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbGroup.java deleted file mode 100644 index 1ceddb9e..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbGroup.java +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.base.AbstractGroup; -import org.linguafranca.pwdb.kdbx.jaxb.binding.JaxbEntryBinding; -import org.linguafranca.pwdb.kdbx.jaxb.binding.JaxbGroupBinding; -import org.linguafranca.pwdb.kdbx.jaxb.binding.ObjectFactory; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -/** - * Implementation of {@link org.linguafranca.pwdb.Group} for JAXB. - * - *

The class wraps an underlying JAXB generated delegate. - * - * @author jo - */ -@SuppressWarnings("WeakerAccess") -public class JaxbGroup extends AbstractGroup { - - protected JaxbDatabase database; - protected JaxbGroupBinding delegate; - - /** - * Wrap an existing {@link JaxbGroupBinding} in this database - * @param database the database - * @param group a JaxbGroupBinding - */ - public JaxbGroup(JaxbDatabase database, JaxbGroupBinding group) { - this.delegate = group; - this.database = database; - } - - public JaxbGroup(JaxbDatabase database) { - this.database = database; - this.delegate = database.getObjectFactory().createJaxbGroupBinding(); - delegate.setTimes(new ObjectFactory().createTimes()); - delegate.setIconID(0); - delegate.setName(""); - delegate.setUUID(UUID.randomUUID()); - } - - @Override - public boolean isRootGroup() { - return database.getRootGroup().equals(this); - } - - @Override - public boolean isRecycleBin() { - return database.getKeePassFile().getMeta().getRecycleBinUUID().equals(this.getUuid()); - } - - @Override - public JaxbGroup getParent() { - if (delegate.parent == null) { - return null; - } - return new JaxbGroup(database, ((JaxbGroupBinding) delegate.parent)); - } - - @Override - public void setParent(JaxbGroup group) { - if (isRootGroup()) { - throw new IllegalStateException("Cannot add root group to another group"); - } - - if (this.database != group.database) { - throw new IllegalStateException("Must be from same database"); - } - - JaxbGroupBinding parent = (JaxbGroupBinding) group.delegate.parent; - - if (parent != null) { - parent.getGroup().remove(group.delegate); - parent.getTimes().setLastModificationTime(new Date()); - } - - this.delegate.parent = group.delegate; - ((JaxbGroupBinding) this.delegate.parent).getTimes().setLastModificationTime(new Date()); - touch(); - } - - @Override - public List getGroups() { - List result = new ArrayList<>(); - for (JaxbGroupBinding child : delegate.getGroup()) { - result.add(new JaxbGroup(database, child)); - } - return result; - } - - @Override - public int getGroupsCount() { - return delegate.getGroup().size(); - } - - @Override - public JaxbGroup addGroup(JaxbGroup group) { - if (group.isRootGroup()) { - throw new IllegalStateException("Cannot add root group to another group"); - } - if (this.database != group.database) { - throw new IllegalStateException("Must be from same database"); - } - - if (group.getParent() != null) { - group.getParent().delegate.getGroup().remove(group.delegate); - } - group.delegate.parent = this.delegate; - this.delegate.getGroup().add(group.delegate); - touch(); - return group; - } - - @Override - public JaxbGroup removeGroup(JaxbGroup group) { - if (this.database != group.database) { - throw new IllegalStateException("Must be from same database"); - } - delegate.getGroup().remove(group.delegate); - group.delegate.parent = null; - touch(); - return group; - } - - @Override - public List getEntries() { - List result = new ArrayList<>(); - for (JaxbEntryBinding entry : this.delegate.getEntry()) { - result.add(new JaxbEntry(database, entry)); - } - return result; - } - - @Override - public int getEntriesCount() { - return this.delegate.getEntry().size(); - } - - @Override - public JaxbEntry addEntry(JaxbEntry entry) { - if (this.database != entry.database) { - throw new IllegalStateException("Must be from same database"); - } - if (entry.getParent() != null) { - entry.getParent().removeEntry(entry); - } - delegate.getEntry().add(entry.delegate); - entry.delegate.parent = this.delegate; - touch(); - return entry; - } - - @Override - public JaxbEntry removeEntry(JaxbEntry entry) { - delegate.getEntry().remove(entry.delegate); - entry.delegate.parent = null; - return entry; - } - - @Override - public String getName() { - return delegate.getName(); - } - - @Override - public void setName(String name) { - this.delegate.setName(name); - } - - @Override - public UUID getUuid() { - return this.delegate.getUUID(); - } - - @Override - public JaxbIcon getIcon() { - return new JaxbIcon(this.delegate.getIconID()); - } - - @Override - public void setIcon(JaxbIcon icon) { - this.delegate.setIconID(icon.getIndex()); - } - - @NotNull - @Override - public JaxbDatabase getDatabase() { - return database; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - JaxbGroup that = (JaxbGroup) o; - - return database.equals(that.database) && delegate.equals(that.delegate); - } - - private void touch() { - this.delegate.getTimes().setLastModificationTime(new Date()); - this.database.setDirty(true); - } -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbIcon.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbIcon.java deleted file mode 100644 index c558be3b..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbIcon.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.linguafranca.pwdb.Icon; - -/** - * @author jo - */ -@SuppressWarnings("WeakerAccess") -public class JaxbIcon implements Icon{ - private int index; - - public JaxbIcon(int iconID) { - index = iconID; - } - - public JaxbIcon() { - index = 0; - } - - @Override - public int getIndex() { - return index; - } - - @Override - public void setIndex(int index) { - this.index = index; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - JaxbIcon that = (JaxbIcon) o; - - return index == that.index; - - } - - @Override - public int hashCode() { - return index; - } -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabase.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabase.java deleted file mode 100644 index 188af88b..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabase.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import com.sun.xml.txw2.output.IndentingXMLStreamWriter; -import org.apache.commons.codec.binary.Base64; -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.SerializableDatabase; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.jaxb.base.ValueBinding; -import org.linguafranca.pwdb.kdbx.jaxb.binding.*; -import org.linguafranca.pwdb.security.StreamEncryptor; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBException; -import javax.xml.bind.Marshaller; -import javax.xml.bind.Unmarshaller; -import javax.xml.stream.XMLOutputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.XMLStreamWriter; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * @author jo - */ -@SuppressWarnings("WeakerAccess") -public class JaxbSerializableDatabase implements SerializableDatabase { - - private final ObjectFactory objectFactory = new ObjectFactory(); - protected KeePassFile keePassFile; - private StreamEncryptor encryption; - - public JaxbSerializableDatabase() { - - } - - public JaxbSerializableDatabase(KeePassFile keePassFile) { - this.keePassFile = keePassFile; - } - - public static void addBinary(KeePassFile keePassFile, ObjectFactory objectFactory, int index, byte[] value) { - // create a new binary to put in the store - Binaries.Binary newBin = objectFactory.createBinariesBinary(); - newBin.setID(index); - newBin.setValue(Helpers.zipBinaryContent(value)); - newBin.setCompressed(true); - if (keePassFile.getMeta().getBinaries() == null) { - keePassFile.getMeta().setBinaries(objectFactory.createBinaries()); - } - keePassFile.getMeta().getBinaries().getBinary().add(newBin); - } - - @Override - public JaxbSerializableDatabase load(InputStream inputStream) { - try { - JAXBContext jc = JAXBContext.newInstance(KeePassFile.class, ValueBinding.class); - Unmarshaller u = jc.createUnmarshaller(); - u.setListener(new Unmarshaller.Listener() { - @Override - public void afterUnmarshal(Object target, Object parent) { - if (target instanceof StringField.Value) { - StringField.Value value = (StringField.Value) target; - if (value.getProtected() != null && value.getProtected()) { - byte[] encrypted = Base64.decodeBase64(value.getValue().getBytes()); - String decrypted = new String(encryption.decrypt(encrypted), StandardCharsets.UTF_8); - value.setValue(decrypted); - value.setProtected(null); - value.protectOnOutput=true; - } - } - if (target instanceof JaxbGroupBinding && (parent instanceof JaxbGroupBinding)) { - ((JaxbGroupBinding) target).parent = ((JaxbGroupBinding) parent); - } - if (target instanceof JaxbEntryBinding && (parent instanceof JaxbGroupBinding)) { - ((JaxbEntryBinding) target).parent = ((JaxbGroupBinding) parent); - } - } - }); - keePassFile = (KeePassFile) u.unmarshal(inputStream); - return this; - } catch (JAXBException e) { - throw new IllegalStateException(e); - } - } - - @Override - public void save(OutputStream outputStream) { - - try { - JAXBContext jc = JAXBContext.newInstance(KeePassFile.class); - Marshaller marshaller = jc.createMarshaller(); - marshaller.setListener(createMarshallerListener(getToEncrypt())); - - XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newInstance(); - XMLStreamWriter xmlStreamWriter = xmlOutputFactory.createXMLStreamWriter(outputStream); - - // this tidies up output of boolean ="False" attributes and indentation, which is incorrect - // in marshaller and also uses tabs rather than spaces, which is more economical - IndentingXMLStreamWriter writer = new IndentingXMLStreamWriter(xmlStreamWriter){ - - @Override - public void writeStartDocument() throws XMLStreamException { - setIndentStep("\t"); - super.writeStartDocument(); - } - - @Override - public void writeAttribute(String localName, String value) throws XMLStreamException { - if (localName.equals("ProtectInMemory")) { - return; - } - if (localName.equals("Protected")) { - if (!value.equalsIgnoreCase("true")){ - return; - } - } - super.writeAttribute(localName, value); - } - }; - marshaller.marshal(keePassFile, writer); - - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - /** - * Creates a listener for marshalling that will rewrite fields that need encryption - */ - private Marshaller.Listener createMarshallerListener(List toEncrypt) { - return new Marshaller.Listener() { - String savedValue=""; - - // this changes the content on save, so we need to change it back again, see comment below - @Override - public void beforeMarshal(Object source) { - if (source instanceof StringField) { - StringField field = (StringField) source; - if (toEncrypt.contains(field.getKey()) || field.getValue().protectOnOutput) { - savedValue = field.getValue().getValue(); - byte [] encrypted = encryption.encrypt(field.getValue().getValue().getBytes(StandardCharsets.UTF_8)); - byte [] base64Encoded = Base64.encodeBase64(encrypted); - field.getValue().setValue(new String(base64Encoded)); - field.getValue().setProtected(true); - } else { - field.getValue().setProtected(false); - } - field.getValue().setProtectInMemory(false); - } - } - - // turns out that undoing the content change we made in beforeMarshal is the easiest way of doing this - // after a couple of days of looking at it. Making a clone before serialization - // is not practical and creating an adapter is not practical either, believe me, I tried. - // That said, if you are a JAXB whizz, and you know better ... - @Override - public void afterMarshal(Object source) { - if (source instanceof StringField) { - StringField field = (StringField) source; - if (field.getValue().getProtected()) { - field.getValue().setValue(savedValue); - field.getValue().setProtected(false); - } - } - } - }; - } - - /** - * Create a list of names of properties that should be encrypted by default - */ - @NotNull - private List getToEncrypt() { - final List toEncrypt = new ArrayList<>(); - if (keePassFile.getMeta().getMemoryProtection().getProtectTitle()) { - toEncrypt.add(org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME_TITLE); - } - if (keePassFile.getMeta().getMemoryProtection().getProtectURL()) { - toEncrypt.add(org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME_URL); - } - if (keePassFile.getMeta().getMemoryProtection().getProtectUserName()) { - toEncrypt.add(org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME_USER_NAME); - } - if (keePassFile.getMeta().getMemoryProtection().getProtectPassword()) { - toEncrypt.add(org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME_PASSWORD); - } - if (keePassFile.getMeta().getMemoryProtection().getProtectNotes()) { - toEncrypt.add(org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME_NOTES); - } - return toEncrypt; - } - - - @Override - public StreamEncryptor getEncryption() { - return encryption; - } - - @Override - public void setEncryption(StreamEncryptor encryption) { - this.encryption = encryption; - } - - @Override - public byte[] getHeaderHash() { - return keePassFile.getMeta().getHeaderHash(); - } - - @Override - public void setHeaderHash(byte[] hash) { - keePassFile.getMeta().setHeaderHash(hash); - } - - @Override - public void addBinary(int index, byte[] value) { - addBinary(keePassFile, objectFactory, index, value); - } - - @Override - public byte[] getBinary(int index) { - return keePassFile.getMeta().getBinaries().getBinary().get(index).getValue(); - } - - @Override - public int getBinaryCount() { - if (Objects.isNull(keePassFile.getMeta().getBinaries())) { - return 0; - } - return keePassFile.getMeta().getBinaries().getBinary().size(); - } - - public KeePassFile getKeePassFile() { - return keePassFile; - } - - public void setKeePassFile(KeePassFile keypassFile) { - this.keePassFile = keypassFile; - } -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/AbstractJaxbParentedBinding.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/AbstractJaxbParentedBinding.java deleted file mode 100644 index f9cb3ae1..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/AbstractJaxbParentedBinding.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb.base; - -import javax.xml.bind.annotation.XmlTransient; - -/** - * Ancestor class for Entry and Group Jaxb Bindings, allowing insertion of - * a reference to the parent group. - * - * @author jo - */ -public abstract class AbstractJaxbParentedBinding { - @XmlTransient - public AbstractJaxbParentedBinding parent; -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/ValueBinding.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/ValueBinding.java deleted file mode 100644 index a89fc2a2..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/base/ValueBinding.java +++ /dev/null @@ -1,29 +0,0 @@ -package org.linguafranca.pwdb.kdbx.jaxb.base; - -import javax.xml.bind.annotation.XmlAccessType; -import javax.xml.bind.annotation.XmlAccessorType; -import javax.xml.bind.annotation.XmlTransient; -import javax.xml.bind.annotation.XmlValue; - -/** - * Ancestor class of StringField.Value, providing a way of flagging protection - * separate from @Protected and @ProtectInMemory - */ -@XmlAccessorType(XmlAccessType.FIELD) -public abstract class ValueBinding { - - @XmlTransient - public boolean protectOnOutput; - - @XmlValue - public String value; - - public String getValue(){ - return value; - } - - public void setValue(String string){ - value = string; - } - -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/DateAdapter.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/DateAdapter.java deleted file mode 100644 index da5f78b3..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/DateAdapter.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb.util; - -import org.linguafranca.pwdb.kdbx.Helpers; - -import java.util.Date; - -/** - * Used for loading the database template which contains placeholders. - * @author jo - */ -public class DateAdapter { - public static String toString(Date date) { - return Helpers.fromDate(date); - } - - public static Date fromString(String string) { - // this is found in the base template - if (string.equals("${creationDate}")) { - return new Date(); - } - return Helpers.toDate(string); - } -} diff --git a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/JaxbUtil.java b/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/JaxbUtil.java deleted file mode 100644 index 7eb700f1..00000000 --- a/jaxb/src/main/java/org/linguafranca/pwdb/kdbx/jaxb/util/JaxbUtil.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.linguafranca.pwdb.kdbx.jaxb.util; - -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.JAXBException; -import javax.xml.bind.util.JAXBSource; -import javax.xml.namespace.QName; - -public class JaxbUtil { - - // https://stackoverflow.com/questions/879453/how-to-make-a-deep-copy-of-jaxb-object-like-xmlbean-xmlobject-copy - // however it does not copy transients which we need it to, so we don't use it - public static T deepCopyJAXB(T object, Class clazz) { - try { - JAXBContext jaxbContext = JAXBContext.newInstance(clazz); - JAXBElement contentObject = new JAXBElement(new QName(clazz.getSimpleName()), clazz, object); - JAXBSource source = new JAXBSource(jaxbContext, contentObject); - return jaxbContext.createUnmarshaller().unmarshal(source, clazz).getValue(); - } catch (JAXBException e) { - throw new RuntimeException(e); - } - } - - public static T deepCopyJAXB(T object) { - if(object==null) throw new RuntimeException("Can't guess at class"); - return deepCopyJAXB(object, (Class) object.getClass()); - } -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBasicDatabaseTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBasicDatabaseTest.java deleted file mode 100644 index d4d08454..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBasicDatabaseTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.linguafranca.pwdb.checks.BasicDatabaseChecks; -import org.linguafranca.pwdb.Database; - -import java.io.IOException; - -/** - * @author jo - */ -public class JaxbBasicDatabaseTest extends BasicDatabaseChecks { - - public JaxbBasicDatabaseTest() throws IOException { - } - - @Override - public Database createDatabase() { - return new JaxbDatabase(); - } -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV3Test.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV3Test.java deleted file mode 100644 index d014cc9b..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV3Test.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.Credentials; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -public class JaxbBinaryPropertyV3Test extends BinaryPropertyChecks { - - public JaxbBinaryPropertyV3Test() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); - database = JaxbDatabase.load(new KdbxCreds("123".getBytes()),inputStream); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials,outputStream); - } - - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return JaxbDatabase.load(credentials, inputStream); - } - - @Override - public Database newDatabase() { - return new JaxbDatabase(); - } - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV4Test.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV4Test.java deleted file mode 100644 index 9a9fcd26..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbBinaryPropertyV4Test.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.junit.Ignore; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -@Ignore -public class JaxbBinaryPropertyV4Test extends BinaryPropertyChecks { - - public JaxbBinaryPropertyV4Test() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-Argon2-Attachment.kdbx"); - database = JaxbDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - StreamFormat sf = new KdbxStreamFormat(new KdbxHeader(4)); - database.save(sf, credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return JaxbDatabase.load(credentials, inputStream); - } - - @Override - public Database newDatabase() { - return new JaxbDatabase(); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } -} \ No newline at end of file diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbLoaderTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbLoaderTest.java deleted file mode 100644 index c3a83ced..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbLoaderTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.linguafranca.pwdb.checks.DatabaseLoaderChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.IOException; -import java.io.InputStream; - -/** - * @author jo - */ -public class JaxbLoaderTest extends DatabaseLoaderChecks { - - public JaxbLoaderTest() throws IOException { - // get an input stream from kdbx file - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - // file has password credentials - KdbxCreds credentials = new KdbxCreds("123".getBytes()); - // open database. DomDatabaseWrapper is so-called, since it wraps - // a W3C DOM, populated from the KeePass XML, and presents it - // through a org.linguafranca.keepass.Database interface. - super.database = JaxbDatabase.load(credentials, inputStream); - } -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbPropertyValueTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbPropertyValueTest.java deleted file mode 100644 index 06f0d611..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbPropertyValueTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.checks.PropertyValueChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class JaxbPropertyValueTest extends PropertyValueChecks { - - public JaxbPropertyValueTest() throws IOException { - super(false); - } - - - @Override - public Database createDatabase() throws IOException { - return new JaxbDatabase(); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - try { - return JaxbDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new IOException(e); - } - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbRecycleBinTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbRecycleBinTest.java deleted file mode 100644 index c837e3c6..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbRecycleBinTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.linguafranca.pwdb.checks.RecycleBinChecks; - -/** - * @author jo - */ -public class JaxbRecycleBinTest extends RecycleBinChecks { - public JaxbRecycleBinTest() { - database = new JaxbDatabase(); - } -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSaveAndReloadTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSaveAndReloadTest.java deleted file mode 100644 index 37eb4341..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSaveAndReloadTest.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.checks.SaveAndReloadChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxHeader; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -public class JaxbSaveAndReloadTest extends SaveAndReloadChecks { - @Override - public JaxbDatabase getDatabase() { - return new JaxbDatabase(); - } - @Override - public JaxbDatabase getDatabase(String name, Credentials credentials) throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name); - return JaxbDatabase.load(credentials, inputStream); - } - - @Override - public void saveDatabase(JaxbDatabase database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public JaxbDatabase loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return JaxbDatabase.load(credentials, inputStream); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - - - @Override - public boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2) { - KdbxHeader h1 = (KdbxHeader) s1.getStreamConfiguration(); - KdbxHeader h2 = (KdbxHeader) s1.getStreamConfiguration(); - return (h1.getVersion() == h2.getVersion() && - h1.getProtectedStreamAlgorithm().equals(h2.getProtectedStreamAlgorithm()) && - h1.getKeyDerivationFunction().equals(h2.getKeyDerivationFunction()) && - h1.getCipherAlgorithm().equals(h2.getCipherAlgorithm())); - } -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabaseTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabaseTest.java deleted file mode 100644 index 97921e51..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbSerializableDatabaseTest.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.junit.Test; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.InputStream; -import java.io.PrintStream; - -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class JaxbSerializableDatabaseTest { - - static PrintStream printStream = getTestPrintStream(); - - @Test - public void createEmptyDatabase() throws Exception { - JaxbDatabase db = JaxbDatabase.createEmptyDatabase(); - db.save(new StreamFormat.None(), new KdbxCreds.None(), printStream); - } - @Test - public void loadXml() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - JaxbDatabase database = JaxbDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); - database.save(new StreamFormat.None(), new KdbxCreds.None(), printStream); - } - -} \ No newline at end of file diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbTest.java deleted file mode 100644 index 60aa341d..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbTest.java +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.junit.Test; -import org.linguafranca.pwdb.kdbx.jaxb.binding.KeePassFile; -import org.linguafranca.pwdb.kdbx.jaxb.binding.StringField; - -import javax.xml.bind.*; -import java.io.PrintStream; - -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class JaxbTest { - static PrintStream printStream = getTestPrintStream(); - - @Test - public void unmarshal() throws JAXBException { - JAXBContext jc = JAXBContext.newInstance(KeePassFile.class); - Unmarshaller u = jc.createUnmarshaller(); - u.setEventHandler(new ValidationEventHandler() { - @Override - public boolean handleEvent(ValidationEvent event) { - printStream.println(event.getLocator().getLineNumber() +": "+ event.getMessage()); - return true; - } - }); - u.setListener(new Unmarshaller.Listener() { - @Override - public void afterUnmarshal(Object target, Object parent) { - if (target instanceof StringField.Value) { - StringField.Value value = (StringField.Value) target; - printStream.println(value.getValue()); - } - super.afterUnmarshal(target, parent); - } - }); - KeePassFile kpf = (KeePassFile) u.unmarshal(getClass().getClassLoader().getResourceAsStream("ExampleDatabase.xml")); - printStream.println(kpf.getMeta().getDatabaseDescription()); - - } -} diff --git a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbV4LoadTest.java b/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbV4LoadTest.java deleted file mode 100644 index 66ecd37e..00000000 --- a/jaxb/src/test/java/org/linguafranca/pwdb/kdbx/jaxb/JaxbV4LoadTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.linguafranca.pwdb.kdbx.jaxb; - -import org.junit.Test; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.InputStream; -import java.io.PrintStream; - -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class JaxbV4LoadTest { - - static PrintStream printStream = getTestPrintStream(); - - @Test - public void loadKdbxV4() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); - JaxbDatabase database = JaxbDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); - // test what happens to dates in V4 - database.visit(new Visitor.Default(){ - @Override - public void visit(Entry entry) { - printStream.println(entry.getCreationTime()); - } - }); - } - -} diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index 3df785cb..1810cb63 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -32,7 +32,7 @@ * The class holds a KDB Group * @author jo */ -public class KdbGroup extends AbstractGroup { +public class KdbGroup extends AbstractGroup { private boolean root; private KdbGroup parent; protected KdbDatabase database; diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomHelper.java b/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomHelper.java deleted file mode 100644 index 7fcb9f70..00000000 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/dom/DomHelper.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.dom; - -import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; -import java.time.Instant; -import java.util.*; - -/** - * The class contains static helper methods for access to the underlying XML DOM - * - * @author jo - */ -public class DomHelper { - - public static XPath xpath = XPathFactory.newInstance().newXPath(); - - static final String GROUP_ELEMENT_NAME = "Group"; - static final String ENTRY_ELEMENT_NAME = "Entry"; - static final String ICON_ELEMENT_NAME = "IconID"; - static final String UUID_ELEMENT_NAME = "UUID"; - static final String NAME_ELEMENT_NAME = "Name"; - static final String NOTES_ELEMENT_NAME = "Notes"; - static final String TIMES_ELEMENT_NAME = "Times"; - static final String IS_EXPANDED = "IsExpanded"; - - static final String HISTORY_ELEMENT_NAME = "History"; - - static final String LAST_MODIFICATION_TIME_ELEMENT_NAME = "Times/LastModificationTime"; - static final String CREATION_TIME_ELEMENT_NAME = "Times/CreationTime"; - static final String LAST_ACCESS_TIME_ELEMENT_NAME = "Times/LastAccessTime"; - static final String EXPIRY_TIME_ELEMENT_NAME = "Times/ExpiryTime"; - static final String EXPIRES_ELEMENT_NAME = "Times/Expires"; - static final String USAGE_COUNT_ELEMENT_NAME = "Times/UsageCount"; - static final String LOCATION_CHANGED = "Times/LocationChanged"; - - static final String PROPERTY_ELEMENT_FORMAT = "String[Key/text()='%s']"; - static final String BINARY_PROPERTY_ELEMENT_FORMAT = "Binary[Key/text()='%s']"; - static final String VALUE_ELEMENT_NAME = "Value"; - - static final String RECYCLE_BIN_UUID_ELEMENT_NAME = "RecycleBinUuid"; - static final String RECYCLE_BIN_ENABLED_ELEMENT_NAME = "RecycleBinEnabled"; - static final String RECYCLE_BIN_CHANGED_ELEMENT_NAME = "RecycleBinChanged"; - - interface ValueCreator { - String getValue(); - } - - static class ConstantValueCreator implements ValueCreator { - String value; - ConstantValueCreator(String value) { - this.value = value; - } - @Override - public String getValue() { - return value; - } - } - - static class DateValueCreator implements ValueCreator { - @Override - public String getValue() { - return Helpers.fromDate(Date.from(Instant.now())); - } - } - - static class UuidValueCreator implements ValueCreator { - @Override - public String getValue() { - return base64RandomUuid(); - } - - } - - static void ensureElements (Element element, Map childElements) { - for (Map.Entry entry: childElements.entrySet()) { - ensureElementContent(entry.getKey(), element, entry.getValue().getValue()); - } - } - - - @Nullable @Contract("_,_,true -> !null") - static Element getElement(String elementPath, Element parentElement, boolean create) { - try { - Element result = (Element) xpath.evaluate(elementPath, parentElement, XPathConstants.NODE); - if (result == null && create) { - result = createHierarchically(elementPath, parentElement); - } - return result; - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - } - - static boolean removeElement(String elementPath, Element parentElement) { - Element toRemove = getElement(elementPath, parentElement, false); - if (toRemove == null) { - return false; - } else { - toRemove.getParentNode().removeChild(toRemove); - return true; - } - } - - static List getElements (String elementPath, Element parentElement) { - try { - NodeList nodes = (NodeList) xpath.evaluate(elementPath, parentElement, XPathConstants.NODESET); - ArrayList result = new ArrayList<>(nodes.getLength()); - for (int i = 0; i < nodes.getLength(); i++) { - result.add(((Element) nodes.item(i))); - } - return result; - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - } - - static int getElementsCount (String elementPath, Element parentElement) { - try { - NodeList nodes = (NodeList) xpath.evaluate(elementPath, parentElement, XPathConstants.NODESET); - return nodes.getLength(); - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - } - - @Nullable - static Element newElement(String elementName, Element parentElement) { - Element newElement = parentElement.getOwnerDocument().createElement(elementName); - parentElement.appendChild(newElement); - return newElement; - } - - @Nullable - public static String getElementContent(String elementPath, Element parentElement) { - Element result = getElement(elementPath, parentElement, false); - return (result == null) ? null : result.getTextContent(); - } - - @NotNull - public static String ensureElementContent(String elementPath, Element parentElement, @NotNull String value) { - Element result = getElement(elementPath, parentElement, false); - if (result == null) { - result = createHierarchically(elementPath, parentElement); - } - result.setTextContent(value); - return result.getTextContent(); - } - - @NotNull - public static Element setElementContent(String elementPath, Element parentElement, String value) { - Element result = getElement(elementPath, parentElement, true); - result.setTextContent(value); - return result; - } - - @Nullable - static byte[] getBinaryElementContent(String elementPath, Element parentElement) { - Element result = getElement(elementPath, parentElement, false); - if (result == null) { - return null; - } - String id = result.getAttribute("Ref"); - Element content = getElement("//Binaries/Binary[@ID=" + id + "]", parentElement.getOwnerDocument().getDocumentElement(),false); - if (content == null) { - throw new IllegalStateException("Could not find binary content with ID " + id); - } - return Helpers.decodeBase64Content(content.getTextContent().getBytes(), content.hasAttribute("Compressed")); - } - - @NotNull - static Element setBinaryElementContent(String elementPath, Element parentElement, byte[] value) { - try { - String b64 = Helpers.encodeBase64Content(value, true); - - //Find the highest numbered existing content - String max = xpath.evaluate("//Binaries/Binary/@ID[not(. < ../../Binary/@ID)][1]", parentElement.getOwnerDocument().getDocumentElement()); - if (max.trim().isEmpty()){ - max = "-1"; - } - Integer newIndex = Integer.valueOf(max) + 1; - - addBinary(parentElement.getOwnerDocument().getDocumentElement(), b64, newIndex); - - Element result = getElement(elementPath, parentElement, true); - result.setAttribute("Ref", newIndex.toString()); - - - return result; - - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - } - - /** - * Add a binary property value to the V3 Meta/Binaries element - * - * @param documentElement the document element - * @param b64 a base64 gzipped encoded representation of the binary content - * @param index the index by which it is known - */ - public static void addBinary(Element documentElement, String b64, Integer index) { - Element binaries = getElement("Meta/Binaries", documentElement,true); - Element binary = (Element) binaries.appendChild(binaries.getOwnerDocument().createElement("Binary")); - binary.setTextContent(b64); - binary.setAttribute("Compressed", "True"); - binary.setAttribute("ID", index.toString()); - } - - public static String getBinary(Element documentElement, Integer index) { - Element binaries = getElement("Meta/Binaries", documentElement,false); - if (Objects.isNull(binaries)){ - throw new IllegalArgumentException("No binaries found"); - } - for (int i = 0; i < binaries.getChildNodes().getLength(); i++){ - if (((Element) binaries.getChildNodes().item(i)).getAttribute("ID").equals(index.toString())) { - return ((Element) binaries.getChildNodes().item(i)).getTextContent(); - } - } - throw new IllegalArgumentException("No binary with that index found"); - } - - public static int getBinaryCount(Element documentElement) { - Element binaries = getElement("Meta/Binaries", documentElement,false); - if (Objects.isNull(binaries)) { - return 0; - } - return binaries.getChildNodes().getLength(); - } - - @NotNull - static Element touchElement(String elementPath, Element parentElement) { - return setElementContent(elementPath, parentElement, Helpers.fromDate(new Date())); - } - - private static Element createHierarchically(String elementPath, Element startElement) { - Element currentElement = startElement; - for (String elementName : elementPath.split("/")) { - try { - Element nextElement = (Element) xpath.evaluate(elementName, currentElement, XPathConstants.NODE); - if (nextElement == null) { - nextElement = (Element) currentElement.appendChild(currentElement.getOwnerDocument().createElement(elementName)); - } - currentElement = nextElement; - } catch (XPathExpressionException e) { - throw new IllegalStateException(e); - } - } - return currentElement; - } - - static String base64RandomUuid () { - return Helpers.base64FromUuid(UUID.randomUUID()); - } - -} diff --git a/pom.xml b/pom.xml index e74cd2b5..bab30c91 100644 --- a/pom.xml +++ b/pom.xml @@ -26,9 +26,6 @@ test kdb kdbx - dom - simple - jaxb jackson all example diff --git a/simple/pom.xml b/simple/pom.xml deleted file mode 100644 index 6e8e5078..00000000 --- a/simple/pom.xml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - KeePassJava2-parent - org.linguafranca.pwdb - 2.2.3-SNAPSHOT - ../pom.xml - - 4.0.0 - - KeePassJava2-simple - KeePassJava2 :: Simple - Contains a Simple XML framework implementation of KDBX. - - - - org.linguafranca.pwdb - KeePassJava2-kdbx - ${project.version} - - - org.linguafranca.pwdb - test - ${project.version} - test - - - junit - junit - test - - - com.carrotsearch.thirdparty - simple-xml-safe - 2.7.1 - - - org.apache.httpcomponents - httpcore - 4.4.16 - - - com.fasterxml - aalto-xml - 1.3.2 - - - \ No newline at end of file diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabase.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabase.java deleted file mode 100644 index 7cafa52a..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabase.java +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.StreamConfiguration; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.base.AbstractDatabase; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; -import org.linguafranca.pwdb.kdbx.simple.model.KeePassFile; -import org.linguafranca.pwdb.security.Encryption; -import org.linguafranca.pwdb.security.StreamEncryptor; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Date; -import java.util.List; -import java.util.Objects; -import java.util.UUID; - -import static org.linguafranca.pwdb.kdbx.simple.SimpleSerializableDatabase.createEmptyDatabase; -import static org.linguafranca.pwdb.kdbx.simple.SimpleSerializableDatabase.getSerializer; - -/** - * Implementation of {@link org.linguafranca.pwdb.Database} using the Simple XML framework. - * - * @author jo - */ -@SuppressWarnings("WeakerAccess") -public class SimpleDatabase extends AbstractDatabase{ - - KeePassFile keePassFile; - StreamFormat streamFormat; - - /** - * Create a new empty database - */ - public SimpleDatabase() { - this(createEmptyDatabase(), null); - } - - public SimpleDatabase(KeePassFile file, StreamFormat streamFormat) { - try { - keePassFile = file; - keePassFile.root.group.database = this; - this.streamFormat = streamFormat; - SimpleSerializableDatabase.fixUp(keePassFile.root.group); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - /** - * Load plaintext XML - * - * @param inputStream contains the XML - * @return a new Database - * @throws Exception on load failure - */ - public static SimpleDatabase loadXml(InputStream inputStream) throws Exception { - KeePassFile result = getSerializer(new StreamEncryptor.None()).read(KeePassFile.class, inputStream); - result.root.group.uuid = UUID.randomUUID(); - return new SimpleDatabase(result, null); - } - - /** - * Load kdbx file - * - * @param credentials credentials to use - * @param inputStream where to load from - * @return a new database - */ - public static SimpleDatabase load(Credentials credentials, InputStream inputStream) throws IOException { - SimpleSerializableDatabase simpleSerializableDatabase = new SimpleSerializableDatabase(); - StreamFormat streamFormat = new KdbxStreamFormat(); - streamFormat.load(simpleSerializableDatabase, credentials, inputStream); - return new SimpleDatabase(simpleSerializableDatabase.getKeePassFile(), streamFormat); - } - - /** - * Save the database with the same stream format that it was loaded with, or V4 default if none - * @param credentials credentials to use - * @param outputStream where to write to - */ - @Override - public void save(Credentials credentials, OutputStream outputStream) throws IOException { - // save with the stream format used to load if it exists, otherwise save V4 - if (Objects.isNull(streamFormat)) { - streamFormat = new KdbxStreamFormat(new KdbxHeader(4)); - } - save(streamFormat, credentials, outputStream); - } - - /** - * Save the database with a choice of stream format - * @param streamFormat the format to use - * @param credentials credentials to use - * @param outputStream where to write to - */ - @Override - public void save(StreamFormat streamFormat, Credentials credentials, - OutputStream outputStream) throws IOException{ - keePassFile.meta.generator = "KeePassJava2-Simple"; - SimpleSerializableDatabase simpleSerializableDatabase = new SimpleSerializableDatabase(this.keePassFile); - streamFormat.save(simpleSerializableDatabase, credentials, outputStream); - setDirty(false); - } - - - @Override - public SimpleGroup getRootGroup() { - return keePassFile.root.getGroup(); - } - - @Override - public SimpleGroup newGroup() { - return SimpleGroup.createGroup(this); - } - - @Override - public SimpleEntry newEntry() { - return SimpleEntry.createEntry(this); - } - - @Override - public SimpleIcon newIcon() { - return new SimpleIcon(); - } - - @Override - public SimpleIcon newIcon(Integer integer) { - SimpleIcon ic = newIcon(); - ic.setIndex(integer); - return ic; - } - - @Override - public boolean isRecycleBinEnabled() { - return this.keePassFile.meta.recycleBinEnabled; - } - - @Override - public void enableRecycleBin(boolean enable) { - this.keePassFile.meta.recycleBinEnabled = enable; - } - - @Override - public SimpleGroup getRecycleBin() { - UUID recycleBinUuid = this.keePassFile.meta.recycleBinUUID; - SimpleGroup g = findGroup(recycleBinUuid); - if (g == null && isRecycleBinEnabled()) { - g = newGroup("Recycle Bin"); - getRootGroup().addGroup(g); - this.keePassFile.meta.recycleBinUUID = g.getUuid(); - this.keePassFile.meta.recycleBinChanged = new Date(); - } - return g; - } - - @Override - public String getName() { - return keePassFile.meta.databaseName; - } - - @Override - public void setName(String s) { - keePassFile.meta.databaseName = s; - keePassFile.meta.databaseNameChanged = new Date(); - setDirty(true); - } - - @Override - public String getDescription() { - return keePassFile.meta.databaseDescription; - } - - @Override - public void setDescription(String s) { - keePassFile.meta.databaseDescription = s; - keePassFile.meta.databaseDescriptionChanged = new Date(); - setDirty(true); - } - - @Override - public boolean shouldProtect(String s) { - return keePassFile.meta.memoryProtection.shouldProtect(s); - } - - - public List getBinaries() { - return keePassFile.getBinaries(); - } - - public void addBinary(byte [] bytes, int index) { - SimpleSerializableDatabase.addBinary(this.keePassFile, index, bytes); - } - - public StreamFormat getStreamFormat() { - return streamFormat; - } -} \ No newline at end of file diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleEntry.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleEntry.java deleted file mode 100644 index ff76f94d..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleEntry.java +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.base.AbstractEntry; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.simple.converter.UuidConverter; -import org.linguafranca.pwdb.kdbx.simple.model.EntryClasses; -import org.linguafranca.pwdb.kdbx.simple.model.KeePassFile; -import org.linguafranca.pwdb.kdbx.simple.model.Times; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementList; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Transient; -import org.simpleframework.xml.convert.Convert; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -import static org.linguafranca.pwdb.kdbx.simple.model.EntryClasses.*; - -/** - * Implementation of {@link org.linguafranca.pwdb.Entry} for Simple XML framework - * - * @author jo - */ -@SuppressWarnings({"WeakerAccess", "unused"}) -@Root(name="Entry") -public class SimpleEntry extends AbstractEntry { - @Element(name = "UUID", type=UUID.class) - @Convert(UuidConverter.class) - protected UUID uuid; - @Element(name = "IconID") - protected int iconID; - @Element(name = "CustomIconUUID", type = UUID.class, required=false) - @Convert(UuidConverter.class) - protected UUID customIconUUID; - @Element(name = "ForegroundColor", required=false) - protected String foregroundColor; - @Element(name = "BackgroundColor", required = false) - protected String backgroundColor; - @Element(name = "OverrideURL", required = false) - protected String overrideURL; - @Element(name = "Tags", required = false) - protected String tags; - @Element(name = "Times") - protected Times times; - @ElementList(inline=true) - protected List string; - @ElementList(inline=true, required = false) - protected List binary; - @Element(name = "AutoType", required = false) - protected EntryClasses.AutoType autoType; - @ElementList(name = "History", required = false) - protected List history; - - @Transient - SimpleDatabase database; - @Transient - SimpleGroup parent; - - protected SimpleEntry() { - string = new ArrayList<>(); - binary = new ArrayList<>(); - times = new Times(); - uuid = UUID.randomUUID(); - iconID = 0; - } - - /** - * Factory to create a new {@link SimpleEntry} with no parent - * @param database in which this entry - * @return a detached Entry - */ - public static SimpleEntry createEntry(SimpleDatabase database) { - SimpleEntry result = new SimpleEntry(); - result.database = database; - result.parent = null; - // avoiding setProperty as it does a touch(); - for (String p: STANDARD_PROPERTY_NAMES) { - result.string.add(new EntryClasses.StringProperty(p, new EntryClasses.StringProperty.Value(""))); - } - return result; - } - - @Override - public String getProperty(String s) { - return getStringContent(getStringProperty(s, string)); - } - - @Override - public void setPropertyValue(String s, String s1) { - EntryClasses.StringProperty sp; - if ((sp = getStringProperty(s, string)) != null) { - this.string.remove(sp); - } - this.string.add(new EntryClasses.StringProperty(s, new EntryClasses.StringProperty.Value(s1))); - touch(); - } - - @Override - public boolean removeProperty(String name) throws IllegalArgumentException { - if (STANDARD_PROPERTY_NAMES.contains(name)) throw new IllegalArgumentException("may not remove property: " + name); - - EntryClasses.StringProperty sp = getStringProperty(name, string); - if (sp == null) { - return false; - } else { - this.string.remove(sp); - touch(); - return true; - } - } - - @Override - public List getPropertyNames() { - List result = new ArrayList<>(); - for (EntryClasses.StringProperty property: this.string) { - result.add(property.getKey()); - } - return result; - } - - @Override - public byte[] getBinaryProperty(String s) { - BinaryProperty bp = getBinaryProp(s, binary); - if (bp == null) { - return null; - } - - KeePassFile.Binary binary = null; - for (KeePassFile.Binary b : database.getBinaries()) { - if (b.getId().equals(Integer.valueOf(getBinaryContent(bp)))) { - binary = b; - } - } - if (binary == null) { - return null; - } - return Helpers.decodeBase64Content(binary.getValue().getBytes(), binary.getCompressed()); - } - - @Override - public void setBinaryProperty(String s, byte[] bytes) { - // remove old binary property with same name - BinaryProperty bp = getBinaryProp(s, binary); - if (bp != null){ - binary.remove(bp); - } - - // what is the next free index in the binary store? - Integer max = -1; - for (KeePassFile.Binary binary: database.getBinaries()){ - if (binary.getId() > max) { - max = binary.getId(); - } - } - max++; - - database.addBinary(bytes, max); - - // make a reference to it from the entry - BinaryProperty binaryProperty = new BinaryProperty(); - binaryProperty.setKey(s); - BinaryProperty.Value fieldValue = new BinaryProperty.Value(); - fieldValue.setRef(String.valueOf(max)); - binaryProperty.setValue(fieldValue); - binary.add(binaryProperty); - touch(); - } - - @Override - public boolean removeBinaryProperty(String name) throws UnsupportedOperationException { - BinaryProperty bp = getBinaryProp(name, binary); - if (bp != null) { - binary.remove(bp); - touch(); - return true; - } - return false; - } - - @Override - public List getBinaryPropertyNames() { - List result = new ArrayList<>(); - for (EntryClasses.BinaryProperty property: this.binary) { - result.add(property.getKey()); - } - return result; - } - - @Override - public SimpleGroup getParent() { - return parent; - } - - @Override - public @NotNull UUID getUuid() { - return uuid; - } - - @Override - public SimpleIcon getIcon() { - return new SimpleIcon(iconID); - } - - @Override - public void setIcon(SimpleIcon icon) { - iconID = icon.getIndex(); - } - - @Override - public Date getLastAccessTime() { - return times.getLastAccessTime(); - } - - @Override - public Date getCreationTime() { - return times.getCreationTime(); - } - - @Override - public boolean getExpires() { - return times.getExpires(); - } - - @Override - public void setExpires(boolean expires) { - times.setExpires(expires); - } - - @Override - public Date getExpiryTime() { - return times.getExpiryTime(); - } - - @Override - public void setExpiryTime(Date expiryTime) throws IllegalArgumentException { - if (expiryTime == null) throw new IllegalArgumentException("expiryTime may not be null"); - times.setExpiryTime(expiryTime); - } - - @Override - public Date getLastModificationTime() { - return times.getLastModificationTime(); - } - - @Override - protected void touch() { - this.times.setLastModificationTime(new Date()); - this.database.setDirty(true); - } -} \ No newline at end of file diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleGroup.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleGroup.java deleted file mode 100644 index 5fb36258..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleGroup.java +++ /dev/null @@ -1,244 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.kdbx.simple.converter.KeePassBooleanConverter; -import org.linguafranca.pwdb.kdbx.simple.converter.UuidConverter; -import org.linguafranca.pwdb.kdbx.simple.model.Times; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.ElementList; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.Transient; -import org.simpleframework.xml.convert.Convert; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -/** - * Implementation of {@link Group} using the Simple XML framework. - * @author jo - */ -@SuppressWarnings({"WeakerAccess", "unused"}) -@Root(name = "Group") -public class SimpleGroup extends org.linguafranca.pwdb.base.AbstractGroup { - @Element(name = "UUID", type = UUID.class) - @Convert(UuidConverter.class) - protected UUID uuid; - @Element(name = "Name") - protected String name; - @Element(name = "Notes", required = false) - protected String notes; - @Element(name = "IconID") - protected int iconID; - @Element(name = "CustomIconUUID", type = UUID.class, required=false) - @Convert(UuidConverter.class) - protected UUID customIconUuid; - @Element(name = "Times") - protected Times times; - @Element(name = "IsExpanded", required = false, type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean isExpanded; - @Element(name = "DefaultAutoTypeSequence", required = false) - protected String defaultAutoTypeSequence; - @Element(name = "EnableAutoType", required = false, type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean enableAutoType; - @Element(name = "EnableSearching", required = false, type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean enableSearching; - @Element(name = "LastTopVisibleEntry", required = false, type = UUID.class) - @Convert(UuidConverter.class) - protected UUID lastTopVisibleEntry; - @ElementList(inline = true, required = false) - protected List entry; - @ElementList(inline = true, required = false) - protected List group; - - @Transient - protected SimpleDatabase database; - @Transient - protected SimpleGroup parent; - - protected SimpleGroup() { - entry = new ArrayList<>(); - group = new ArrayList<>(); - times = new Times(); - } - - public static SimpleGroup createGroup(SimpleDatabase database) { - SimpleGroup group = new SimpleGroup(); - group.database = database; - group.iconID = 0; - group.name = ""; - group.uuid = UUID.randomUUID(); - return group; - } - - @Override - public boolean isRootGroup() { - return database.getRootGroup().equals(this); - } - - @Override - public boolean isRecycleBin() { - return database.keePassFile.meta.recycleBinUUID.equals(this.uuid); - } - - @Override - public SimpleGroup getParent() { - return parent; - } - - @Override - public void setParent(SimpleGroup group) { - if (isRootGroup()) { - throw new IllegalStateException("Cannot add root group to another group"); - } - if (this.database != group.database) { - throw new IllegalStateException("Must be from same database"); - } - if (parent != null) { - parent.removeGroup(group); - parent.touch(); - } - parent = group; - parent.touch(); - touch(); - } - - @Override - public List getGroups() { - List result = new ArrayList<>(); - for (SimpleGroup aGroup : group) { - result.add(aGroup); - } - return result; - } - - @Override - public int getGroupsCount() { - return group.size(); - } - - @Override - public SimpleGroup addGroup(SimpleGroup group) { - if (group.isRootGroup()) { - throw new IllegalStateException("Cannot add root group to another group"); - } - if (this.database != group.database) { - throw new IllegalStateException("Must be from same database"); - } - if (group.getParent() != null) { - group.getParent().removeGroup(group); - } - group.parent = this; - this.group.add(group); - touch(); - return group; - } - - @Override - public SimpleGroup removeGroup(SimpleGroup group) { - if (this.database != group.database) { - throw new IllegalStateException("Must be from same database"); - } - this.group.remove(group); - group.parent = null; - touch(); - return group; - } - - @Override - public List getEntries() { - List result = new ArrayList<>(); - for (SimpleEntry entry: this.entry){ - result.add(entry); - } - return result; - } - - @Override - public int getEntriesCount() { - return this.entry.size(); - } - - @Override - public SimpleEntry addEntry(SimpleEntry entry) { - if (this.database != entry.database) { - throw new IllegalStateException("Must be from same database"); - } - if (entry.getParent() != null) { - entry.getParent().removeEntry(entry); - } - this.entry.add(entry); - entry.parent=this; - touch(); - return entry; - } - - @Override - public SimpleEntry removeEntry(SimpleEntry entry) { - if (this.database != entry.database) { - throw new IllegalStateException("Must be from same database"); - } - this.entry.remove(entry); - entry.parent = null; - return entry; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setName(String s) { - this.name = s; - touch(); - } - - @Override - public UUID getUuid() { - return uuid; - } - - @Override - public SimpleIcon getIcon() { - return new SimpleIcon(iconID); - } - - @Override - public void setIcon(SimpleIcon icon) { - this.iconID = icon.getIndex(); - touch(); - } - - @NotNull - @Override - public SimpleDatabase getDatabase() { - return database; - } - - private void touch() { - this.times.setLastModificationTime(new Date()); - this.database.setDirty(true); - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleIcon.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleIcon.java deleted file mode 100644 index 377b822e..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleIcon.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.Icon; - -/** - * @author jo - */ -@SuppressWarnings("WeakerAccess") -public class SimpleIcon implements Icon { - private int index; - - public SimpleIcon() { - } - - public SimpleIcon(int index) { - this.index = index; - } - - @Override - public int getIndex() { - return index; - } - - @Override - public void setIndex(int i) { - index = i; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - SimpleIcon that = (SimpleIcon) o; - - return index == that.index; - - } - - @Override - public int hashCode() { - return index; - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleSerializableDatabase.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleSerializableDatabase.java deleted file mode 100644 index a9d4de14..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/SimpleSerializableDatabase.java +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.SerializableDatabase; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.simple.converter.EmptyStringConverter; -import org.linguafranca.pwdb.kdbx.simple.converter.ValueConverter; -import org.linguafranca.pwdb.kdbx.simple.model.EntryClasses; -import org.linguafranca.pwdb.kdbx.simple.model.KeePassFile; -import org.linguafranca.pwdb.security.StreamEncryptor; -import org.simpleframework.xml.Serializer; -import org.simpleframework.xml.convert.AnnotationStrategy; -import org.simpleframework.xml.convert.Registry; -import org.simpleframework.xml.convert.RegistryStrategy; -import org.simpleframework.xml.core.Persister; -import org.simpleframework.xml.strategy.Strategy; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.util.Objects; - -/** - * @author jo - */ -@SuppressWarnings("WeakerAccess") -public class SimpleSerializableDatabase implements SerializableDatabase { - - public KeePassFile keePassFile; - private StreamEncryptor encryption; - - public SimpleSerializableDatabase(){ - - } - public SimpleSerializableDatabase(KeePassFile keePassFile) { - this.keePassFile = keePassFile; - } - - - /** - * Create an empty underlying KeePassFile instance - * - * @return a new database - */ - static KeePassFile createEmptyDatabase() { - InputStream inputStream = SimpleDatabase.class.getClassLoader().getResourceAsStream("base.kdbx.xml"); - try { - return getSerializer(new StreamEncryptor.None()).read(KeePassFile.class, inputStream); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public SimpleSerializableDatabase load(InputStream inputStream) { - try { - // decrypt the encrypted fields in the inner XML stream - // InputStream plainTextXmlStream = new XmlInputStreamFilter(inputStream, new KdbxInputTransformer(encryption)); - // read the now entirely decrypted stream into database - keePassFile = getSerializer(encryption).read(KeePassFile.class, inputStream); - // ensure that parent fields are set - fixUp(keePassFile.root.group); - return this; - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - @Override - public void save(OutputStream outputStream) throws IOException { - // encrypt the fields in the XML inner stream - // XmlOutputStreamFilter plainTextOutputStream = new XmlOutputStreamFilter(outputStream, new KdbxOutputTransformer(encryption)); - - // set up the "protected" attributes of fields that need inner stream encryption - prepareForSave(keePassFile.root.group); - - // and save the database out - try { - getSerializer(encryption).write(this.keePassFile, outputStream); - } catch (Exception e) { - throw new IOException(e); - } - } - - /** - * Utility to get a simple framework persister - * @return a persister - */ - public static Serializer getSerializer(StreamEncryptor encryption) { - Registry registry = new Registry(); - try { - registry.bind(String.class, EmptyStringConverter.class); - registry.bind(EntryClasses.StringProperty.Value.class, new ValueConverter(encryption)); - } catch (Exception e) { - throw new IllegalStateException(e); - } - Strategy strategy = new AnnotationStrategy(new RegistryStrategy(registry)); - return new Persister(strategy); - } - - /** - * Utility to mark fields that need to be encrypted and vice versa - * - * @param parent the group to start from - */ - private static void prepareForSave(SimpleGroup parent){ - for (SimpleGroup group: parent.group) { - prepareForSave(group); - } - for (SimpleEntry entry: parent.entry) { - for (EntryClasses.StringProperty property : entry.string) { - boolean shouldProtect = parent.database.shouldProtect(property.getKey()); - property.getValue().setProtectOnOutput(shouldProtect || property.getValue().getProtectOnOutput()); - } - if (Objects.nonNull(entry.history)) { - for (SimpleEntry entry2 : entry.history) { - for (EntryClasses.StringProperty property : entry2.string) { - boolean shouldProtect = parent.database.shouldProtect(property.getKey()); - property.getValue().setProtectOnOutput(shouldProtect || property.getValue().getProtectOnOutput()); - } - } - } - } - } - - /** - * On load add parents - * @param parent a parent to recurse - */ - static void fixUp(SimpleGroup parent){ - for (SimpleGroup group: parent.group) { - group.parent = parent; - group.database = parent.database; - fixUp(group); - } - for (SimpleEntry entry: parent.entry) { - entry.database = parent.database; - entry.parent = parent; - } - } - - @Override - public StreamEncryptor getEncryption() { - return encryption; - } - - @Override - public void setEncryption(StreamEncryptor encryption) { - this.encryption = encryption; - } - - @Override - public byte[] getHeaderHash() { - return keePassFile.meta.headerHash.getContent(); - } - - @Override - public void setHeaderHash(byte[] hash) { - keePassFile.meta.headerHash = new KeePassFile.ByteArray(hash); - } - - @Override - public void addBinary(int index, byte[] value) { - addBinary(keePassFile, index, value); - } - - @Override - public byte[] getBinary(int index) { - KeePassFile.Binary binary = keePassFile.getBinaries().get(index); - String value = binary.getValue(); - return Helpers.decodeBase64Content(value.getBytes(), binary.getCompressed()); - } - - @Override - public int getBinaryCount() { - if (Objects.isNull(keePassFile.getBinaries())){ - return 0; - } - return keePassFile.getBinaries().size(); - } - - public static void addBinary(KeePassFile keePassFile, int index, byte[] value) { - // create a new binary to put in the store - KeePassFile.Binary newBin = new KeePassFile.Binary(); - newBin.setId(index); - newBin.setValue(Helpers.encodeBase64Content(value, true)); - newBin.setCompressed(true); - if (keePassFile.getBinaries() == null) { - keePassFile.createBinaries(); - } - keePassFile.getBinaries().add(newBin); - } - - - - public KeePassFile getKeePassFile() { - return keePassFile; - } - - public void setKeePassFile(KeePassFile keypassFile) { - this.keePassFile = keypassFile; - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/Base64ByteArrayConverter.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/Base64ByteArrayConverter.java deleted file mode 100644 index f3fe3b33..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/Base64ByteArrayConverter.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.converter; - -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.simple.model.KeePassFile; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** - * @author jo - */ -public class Base64ByteArrayConverter implements Converter{ - @Override - public KeePassFile.ByteArray read(InputNode inputNode) throws Exception { - String input = inputNode.getValue(); - byte[] value = input == null? new byte[0] : input.getBytes(); - return new KeePassFile.ByteArray(Helpers.decodeBase64Content(value, false)); - } - - @Override - public void write(OutputNode outputNode, KeePassFile.ByteArray bytes) throws Exception { - outputNode.setValue(Helpers.encodeBase64Content(bytes.getContent(), false)); - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/EmptyStringConverter.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/EmptyStringConverter.java deleted file mode 100644 index ab27c06d..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/EmptyStringConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.converter; - -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** - * @author jo - */ -public class EmptyStringConverter implements Converter { - @Override - public String read(InputNode inputNode) throws Exception { - String value = inputNode.getValue(); - return value == null ? "": value; - } - - @Override - public void write(OutputNode outputNode, String s) throws Exception { - if(s==null || s.equals("")) { - outputNode.setValue(null); - } else { - outputNode.setValue(s); - } - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/KeePassBooleanConverter.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/KeePassBooleanConverter.java deleted file mode 100644 index ef7f084f..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/KeePassBooleanConverter.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.converter; - -import org.linguafranca.pwdb.kdbx.Helpers; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -/** - * @author jo - */ -public class KeePassBooleanConverter implements Converter{ - @Override - public Boolean read(InputNode inputNode) throws Exception { - return Helpers.toBoolean(inputNode.getValue()); - } - - @Override - public void write(OutputNode outputNode, Boolean bool) throws Exception { - outputNode.setValue(Helpers.fromBoolean(bool)); - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/TimeConverter.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/TimeConverter.java deleted file mode 100644 index 87f7e06f..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/TimeConverter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.converter; - -import org.linguafranca.pwdb.kdbx.Helpers; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -import java.util.Date; - -/** - * @author jo - */ -public class TimeConverter implements Converter{ - @Override - public Date read(InputNode inputNode) throws Exception { - String value = inputNode.getValue(); - if (value.equals("${creationDate}")) { - return new Date(); - } - return Helpers.toDate(value); - } - - @Override - public void write(OutputNode outputNode, Date date) throws Exception { - outputNode.setValue(Helpers.fromDate(date)); - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/UuidConverter.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/UuidConverter.java deleted file mode 100644 index f211901a..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/UuidConverter.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.converter; - -import org.linguafranca.pwdb.kdbx.Helpers; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -import java.util.UUID; - -/** - * @author jo - */ -public class UuidConverter implements Converter { - @Override - public UUID read(InputNode inputNode) throws Exception { - String value = inputNode.getValue(); - if (value == null || value.equals("")) { - return UUID.randomUUID(); - } - return Helpers.uuidFromBase64(value); - } - - @Override - public void write(OutputNode outputNode, UUID uuid) throws Exception { - outputNode.setValue(Helpers.base64FromUuid(uuid)); - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/ValueConverter.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/ValueConverter.java deleted file mode 100644 index eb436fae..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/converter/ValueConverter.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.linguafranca.pwdb.kdbx.simple.converter; - -import org.apache.commons.codec.binary.Base64; -import org.linguafranca.pwdb.kdbx.simple.model.EntryClasses; -import org.linguafranca.pwdb.security.StreamEncryptor; -import org.simpleframework.xml.convert.Converter; -import org.simpleframework.xml.stream.InputNode; -import org.simpleframework.xml.stream.OutputNode; - -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -/** - * Value converter takes care of encryption and decryption of protected fields - */ -public class ValueConverter implements Converter { - - private final StreamEncryptor encryption; - - public ValueConverter(StreamEncryptor encryption) { - this.encryption = encryption; - } - - /** - * De-serialise (unmarshal) the input node - * @param node this is the node to deserialize the object from - * - * @return the de-serialised object - */ - @Override - public EntryClasses.StringProperty.Value read(InputNode node) throws Exception { - EntryClasses.StringProperty.Value value = new EntryClasses.StringProperty.Value(); - if (Objects.nonNull(node.getAttribute("Protected")) && node.getAttribute("Protected").getValue().equals("True")) { - byte[] encrypted = new byte[0]; - // you can only read the value once - String text = node.getValue(); - if (Objects.nonNull(text)) { - encrypted = Base64.decodeBase64(text.getBytes()); - } - String decrypted = new String(encryption.decrypt(encrypted), StandardCharsets.UTF_8); - value.setText(decrypted); - value.setProtected(null); - value.setProtectOnOutput(true); - } else { - value.setProtectOnOutput(false); - value.setText(node.getValue()); - } - return value; - } - - /** - * Serialise (marshal) the object - * @param node this is the node to serialized the object to - * @param value this is the value that is to be serialized - */ - @Override - public void write(OutputNode node, EntryClasses.StringProperty.Value value) throws Exception { - if (value.getProtectOnOutput()) { - byte [] encrypted = encryption.encrypt(value.getText().getBytes(StandardCharsets.UTF_8)); - byte [] base64Encoded = Base64.encodeBase64(encrypted); - node.setValue(new String(base64Encoded)); - node.getAttributes().put("Protected", "True"); - } else { - node.setValue(value.getText()); - } - //node.getAttributes().remove("protectInMemory"); - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/EntryClasses.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/EntryClasses.java deleted file mode 100644 index 02285fe5..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/EntryClasses.java +++ /dev/null @@ -1,212 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.model; - -import org.linguafranca.pwdb.kdbx.simple.SimpleEntry; -import org.linguafranca.pwdb.kdbx.simple.converter.KeePassBooleanConverter; -import org.simpleframework.xml.*; -import org.simpleframework.xml.convert.Convert; - -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - -/** - * @author jo - */ -@SuppressWarnings("unused") -public abstract class EntryClasses { - public static StringProperty getStringProperty(String name, List string) { - for (StringProperty property : string) { - if (property.key.equals(name)) { - return property; - } - } - return null; - } - - public static String getStringContent(StringProperty property) { - return property == null || property.value == null? null:property.value.text; - } - - public static BinaryProperty getBinaryProp(String name, List binary) { - for (BinaryProperty property : binary) { - if (property.key.equals(name)) { - return property; - } - } - return null; - } - - public static String getBinaryContent(BinaryProperty property) { - return property == null || property.value == null ? null : property.value.ref; - } - - @Root(name = "AutoType") - public static class AutoType { - @Element(name = "Enabled", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean enabled; - @Element(name = "DataTransferObfuscation") - protected int dataTransferObfuscation; - @Element(name = "DefaultSequence", required = false) - protected String defaultSequence; - @Element(name = "Association", required = false) - protected AutoType.Association association; - - public static class Association { - @Element(name = "Window") - protected String window; - @Element(name="KeystrokeSequence") - protected String keystrokeSequence; - protected List windowAndKeystrokeSequence; - } - } - - @Root(name="String") - public static class StringProperty implements org.simpleframework.xml.util.Entry { - - public StringProperty() { - this("",new Value()); - } - public StringProperty(String key, Value value) { - this.key = key; - this.value = value; - } - - @Element(name="Key") - String key; - - @Element(name="Value") - Value value; - - @Override - public String getName() { - return key; - } - - public String getKey() { - return key; - } - - public Value getValue() { - return value; - } - - @Root(name="Value") - public static class Value { - public Value(){ - this(""); - } - public Value(String text) { - this.text = text; - this._protected = false; - } - - public Value(String text, Boolean _protected) { - this._protected = _protected; - this.text = text; - } - - @Attribute(name = "ProtectInMemory", required = false) - @Convert(KeePassBooleanConverter.class) - protected Boolean protectInMemory; - @Attribute(name = "Protected", required = false) - Boolean _protected; - @Attribute(name = "kpj2-ProtectOnOutput", required = false) - @Convert(KeePassBooleanConverter.class) - Boolean protectOnOutput; - @Text - String text; - - public String getText() { - return text; - } - - public void setText(String text) { - this.text = text; - } - - public void setProtectOnOutput(boolean aProtected) { - this.protectOnOutput = aProtected; - } - - public boolean getProtectOnOutput() { - return Objects.nonNull(this.protectOnOutput) && this.protectOnOutput; - } - - - public Boolean getProtected() { - return _protected; - } - - public void setProtected(Boolean _protected) { - this._protected = _protected; - } - } - } - - @Root(name="Binary") - public static class BinaryProperty implements org.simpleframework.xml.util.Entry { - - @Element(name="Key") - String key; - - @Element(name="Value") - Value value; - - public String getKey() { - return key; - } - - @Override - public String getName() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - - public void setValue(Value value) { - this.value = value; - } - - @Root(name="Value") - public static class Value { - @Attribute(name="Ref") - String ref; - - public void setRef(String ref) { - this.ref = ref; - } - } - } - - public static class History { - - @ElementList(entry = "SimpleEntry", inline = true) - private List list; - - public History(){ - list = new ArrayList<>(); - } - public List getHistory(){ - return list; - } - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/KeePassFile.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/KeePassFile.java deleted file mode 100644 index 298f512c..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/KeePassFile.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.model; - -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.kdbx.simple.SimpleGroup; -import org.linguafranca.pwdb.kdbx.simple.converter.Base64ByteArrayConverter; -import org.linguafranca.pwdb.kdbx.simple.converter.KeePassBooleanConverter; -import org.linguafranca.pwdb.kdbx.simple.converter.TimeConverter; -import org.linguafranca.pwdb.kdbx.simple.converter.UuidConverter; -import org.simpleframework.xml.*; -import org.simpleframework.xml.convert.Convert; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.UUID; - -/** - * @author jo - */ -@SuppressWarnings({"WeakerAccess", "unused"}) -@Root(name = "KeePassFile") -public class KeePassFile { - - @Element(name = "Meta") - public Meta meta; - @Element(name = "Root") - public Root root; - - public List getBinaries() { - return meta.binaries; - } - - public void createBinaries() { - meta.binaries = new ArrayList<>(); - } - - public static class Root { - @Element(name = "Group") - public SimpleGroup group; - @ElementList(name = "DeletedObjects", required = false) - protected ArrayList deletedObjects; - - public SimpleGroup getGroup() { - return group; - } - } - - @SuppressWarnings("unused") - public static class Meta { - @Element(name = "Generator") - public String generator; - @Element(name = "HeaderHash", required = false) - @Convert(Base64ByteArrayConverter.class) - public KeePassFile.ByteArray headerHash; - @Element(name = "DatabaseName") - public String databaseName; - @Element(name = "DatabaseNameChanged", type = Date.class) - @Convert(TimeConverter.class) - public Date databaseNameChanged; - @Element(name = "DatabaseDescription") - public String databaseDescription; - @Element(name = "DatabaseDescriptionChanged", type = Date.class) - @Convert(TimeConverter.class) - public Date databaseDescriptionChanged; - @Element(name = "DefaultUserName") - protected String defaultUserName; - @Element(name = "DefaultUserNameChanged", type = Date.class) - @Convert(TimeConverter.class) - protected Date defaultUserNameChanged; - @Element(name = "MaintenanceHistoryDays") - protected int maintenanceHistoryDays; - @Element(name = "Color") - protected String color; - @Element(name = "MasterKeyChanged", type = Date.class) - @Convert(TimeConverter.class) - protected Date masterKeyChanged; - @Element(name = "MasterKeyChangeRec") - protected int masterKeyChangeRec; - @Element(name = "MasterKeyChangeForce") - protected int masterKeyChangeForce; - @Element(name = "MemoryProtection") - public KeePassFile.MemoryProtection memoryProtection; - @ElementList(name = "CustomIcons", required = false) - protected ArrayList customIcons; - @Element(name = "RecycleBinEnabled", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - public Boolean recycleBinEnabled; - @Element(name = "RecycleBinUUID", type = UUID.class) - @Convert(UuidConverter.class) - public UUID recycleBinUUID; - @Element(name = "RecycleBinChanged", type = Date.class) - @Convert(TimeConverter.class) - public Date recycleBinChanged; - @Element(name = "EntryTemplatesGroup", type = UUID.class) - @Convert(UuidConverter.class) - protected UUID entryTemplatesGroup; - @Element(name = "EntryTemplatesGroupChanged", type = Date.class) - @Convert(TimeConverter.class) - protected Date entryTemplatesGroupChanged; - @Element(name = "LastSelectedGroup", type = UUID.class) - @Convert(UuidConverter.class) - protected UUID lastSelectedGroup; - @Element(name = "LastTopVisibleGroup", type = UUID.class) - @Convert(UuidConverter.class) - protected UUID lastTopVisibleGroup; - @Element(name = "HistoryMaxItems") - protected int historyMaxItems; - @Element(name = "HistoryMaxSize") - protected int historyMaxSize; - @ElementList(name = "Binaries", required = false) - protected List binaries; - @Element(name = "CustomData", required = false) - protected KeePassFile.CustomData customData; - - /* version 4 */ - - @Element(name = "SettingsChanged", required = false, type = Date.class) - @Convert(TimeConverter.class) - protected Date settingsChanged; - } - - - public static class MemoryProtection { - @Element(name = "ProtectTitle", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean protectTitle; - @Element(name = "ProtectUserName", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean protectUserName; - @Element(name = "ProtectPassword", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean protectPassword; - @Element(name = "ProtectURL", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean protectURL; - @Element(name = "ProtectNotes", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean protectNotes; - - public Boolean shouldProtect(String name) { - switch(name) { - case Entry.STANDARD_PROPERTY_NAME_TITLE: return protectTitle; - case Entry.STANDARD_PROPERTY_NAME_USER_NAME: return protectUserName; - case Entry.STANDARD_PROPERTY_NAME_PASSWORD: return protectPassword; - case Entry.STANDARD_PROPERTY_NAME_URL: return protectURL; - case Entry.STANDARD_PROPERTY_NAME_NOTES: return protectNotes; - } - return false; - } - } - - public static class Binaries { - - } - - @org.simpleframework.xml.Root(name = "Binary") - public static class Binary implements org.simpleframework.xml.util.Entry { - @Text - protected String value; - - @Attribute(name = "ID") - protected Integer id; - @Attribute(name = "Compressed") - @Convert(KeePassBooleanConverter.class) - protected Boolean compressed; - - @Override - public String getName() { - return String.valueOf(id); - } - - public String getValue() { - return value; - } - - public Boolean getCompressed() { - return compressed; - } - - public Integer getId() { - return id; - } - - public void setId(Integer Id) { - this.id = Id; - } - - public void setValue(String value) { - this.value = value; - } - - public void setCompressed(boolean compressed) { - this.compressed = compressed; - } - } - - @org.simpleframework.xml.Root(name = "Icon") - public static class Icon implements org.simpleframework.xml.util.Entry { - @Element(name = "UUID", type=UUID.class) - @Convert(UuidConverter.class) - protected UUID uuid; - @Element(name = "Name") - protected String name; - @Element(name = "LastModificationTime", type=Date.class) - @Convert(TimeConverter.class) - protected Date lastModificationTime; - @Element(name = "Data") - @Convert(Base64ByteArrayConverter.class) - protected ByteArray data; - - @Override - public String getName() { - return uuid.toString(); - } - } - - /** - * Work around problems deserializing byte[] - */ - public static class ByteArray { - private byte[] content; - - public ByteArray(byte[] content) { - this.content = content; - } - - public byte[] getContent() { - return content; - } - - public void setContent(byte[] content) { - this.content = content; - } - } - - public static class CustomData { - protected List any; - } - - @org.simpleframework.xml.Root(name = "DeletedObject") - public static class DeletedObject { - @Element(name = "UUID", type = UUID.class) - @Convert(UuidConverter.class) - protected UUID uuid; - @Element(name = "DeletionTime", type = Date.class) - @Convert(TimeConverter.class) - protected Date deletionTime; - } -} diff --git a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/Times.java b/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/Times.java deleted file mode 100644 index b96388e4..00000000 --- a/simple/src/main/java/org/linguafranca/pwdb/kdbx/simple/model/Times.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple.model; - -import org.linguafranca.pwdb.kdbx.simple.converter.KeePassBooleanConverter; -import org.linguafranca.pwdb.kdbx.simple.converter.TimeConverter; -import org.simpleframework.xml.Element; -import org.simpleframework.xml.Root; -import org.simpleframework.xml.convert.Convert; - -import java.util.Date; - -/** - * @author jo - */ -@SuppressWarnings({"unused", "WeakerAccess"}) -@Root -public class Times { - @Element(name = "LastModificationTime", type = Date.class) - @Convert(TimeConverter.class) - protected Date lastModificationTime; - @Element(name = "CreationTime", type = Date.class) - @Convert(TimeConverter.class) - protected Date creationTime; - @Element(name = "LastAccessTime", type = Date.class) - @Convert(TimeConverter.class) - protected Date lastAccessTime; - @Element(name = "ExpiryTime", type = Date.class) - @Convert(TimeConverter.class) - protected Date expiryTime; - @Element(name = "Expires", type = Boolean.class) - @Convert(KeePassBooleanConverter.class) - protected Boolean expires; - @Element(name = "UsageCount") - protected int usageCount; - @Element(name = "LocationChanged", type = Date.class) - @Convert(TimeConverter.class) - protected Date locationChanged; - - public Date getLastModificationTime() { - return lastModificationTime; - } - - public void setLastModificationTime(Date lastModificationTime) { - this.lastModificationTime = lastModificationTime; - } - - public Date getCreationTime() { - return creationTime; - } - - public void setCreationTime(Date creationTime) { - this.creationTime = creationTime; - } - - public Date getLastAccessTime() { - return lastAccessTime; - } - - public void setLastAccessTime(Date lastAccessTime) { - this.lastAccessTime = lastAccessTime; - } - - public Date getExpiryTime() { - return expiryTime; - } - - public void setExpiryTime(Date expiryTime) { - this.expiryTime = expiryTime; - } - - public Boolean getExpires() { - return expires; - } - - public void setExpires(Boolean expires) { - this.expires = expires; - } - - public int getUsageCount() { - return usageCount; - } - - public void setUsageCount(int usageCount) { - this.usageCount = usageCount; - } - - public Date getLocationChanged() { - return locationChanged; - } - - public void setLocationChanged(Date locationChanged) { - this.locationChanged = locationChanged; - } - - public Times() { - this(new Date(System.currentTimeMillis()/1000*1000)); - } - public Times(Date date) { - lastModificationTime = date; - lastAccessTime = date; - locationChanged = date; - creationTime = date; - expiryTime = date; - expires = false; - usageCount = 0; - } -} diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV3Test.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV3Test.java deleted file mode 100644 index be8bde9b..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV3Test.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.Credentials; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -public class SimpleBinaryPropertyV3Test extends BinaryPropertyChecks { - - public SimpleBinaryPropertyV3Test() { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); - try { - database = SimpleDatabase.load(new KdbxCreds("123".getBytes()),inputStream); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - try { - return SimpleDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - - @Override - public Database newDatabase() { - return new SimpleDatabase(); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } -} diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV4Test.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV4Test.java deleted file mode 100644 index aca2335d..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleBinaryPropertyV4Test.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.junit.Ignore; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -/** - * @author jo - */ -@Ignore -public class SimpleBinaryPropertyV4Test extends BinaryPropertyChecks { - - public SimpleBinaryPropertyV4Test() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-Argon2-Attachment.kdbx"); - database = SimpleDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - try { - return SimpleDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public Database newDatabase() { - return new SimpleDatabase(); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - -} \ No newline at end of file diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoadTest.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoadTest.java deleted file mode 100644 index 8feb77c5..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoadTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.junit.Ignore; -import org.junit.Test; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.InputStream; -import java.io.PrintStream; - -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class SimpleDatabaseLoadTest { - - static PrintStream printStream = getTestPrintStream(); - - @Test @Ignore(value = "Looks like this example is bogus") - public void loadXml() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("ExampleDatabase.xml"); - SimpleDatabase database = SimpleDatabase.loadXml(inputStream); - database.visit(new Visitor.Print(printStream)); - } - @Test - public void loadKdbx() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - SimpleDatabase database = SimpleDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); - } - @Test - public void loadKdbxV4() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); - SimpleDatabase database = SimpleDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); - // test what happens to dates in V4 - database.visit(new Visitor.Default(){ - @Override - public void visit(Entry entry) { - printStream.println(entry.getCreationTime()); - } - }); - } - - @Test - public void emptyDb() throws Exception { - SimpleDatabase database = new SimpleDatabase(); - printStream.println(database.getDescription()); - } - - @Test - public void dbWithDeleted() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("testDeleted.kdbx"); - SimpleDatabase database = SimpleDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); - } - -} \ No newline at end of file diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoaderTest.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoaderTest.java deleted file mode 100644 index e3946932..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseLoaderTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.checks.DatabaseLoaderChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.Credentials; - -import java.io.InputStream; - -/** - * @author jo - */ -public class SimpleDatabaseLoaderTest extends DatabaseLoaderChecks { - - public SimpleDatabaseLoaderTest() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - // file has password credentials - Credentials credentials = new KdbxCreds("123".getBytes()); - super.database = SimpleDatabase.load(credentials, inputStream); - - } -} diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseTest.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseTest.java deleted file mode 100644 index dea2d1ac..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleDatabaseTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.checks.BasicDatabaseChecks; -import org.linguafranca.pwdb.Database; - -import java.io.IOException; - -/** - * @author jo - */ -public class SimpleDatabaseTest extends BasicDatabaseChecks { - - public SimpleDatabaseTest() throws IOException { - } - - @Override - public Database createDatabase() { - try { - return new SimpleDatabase(); - } catch (Exception e) { - throw new IllegalStateException(); - } - } -} diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimplePropertyValueTest.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimplePropertyValueTest.java deleted file mode 100644 index bd81a9bc..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimplePropertyValueTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2024 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.checks.PropertyValueChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class SimplePropertyValueTest extends PropertyValueChecks { - - public SimplePropertyValueTest() throws IOException { - super(false); - } - - - @Override - public Database createDatabase() throws IOException { - return new SimpleDatabase(); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - try { - return SimpleDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new IOException(e); - } - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } -} diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleRecycleBinTest.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleRecycleBinTest.java deleted file mode 100644 index 3d7a4895..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleRecycleBinTest.java +++ /dev/null @@ -1,12 +0,0 @@ -package org.linguafranca.pwdb.kdbx.simple; - -import org.linguafranca.pwdb.checks.RecycleBinChecks; - -/** - * @author jo - */ -public class SimpleRecycleBinTest extends RecycleBinChecks { - public SimpleRecycleBinTest() { - database = new SimpleDatabase(); - } -} diff --git a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleSaveAndReloadTest.java b/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleSaveAndReloadTest.java deleted file mode 100644 index d848e759..00000000 --- a/simple/src/test/java/org/linguafranca/pwdb/kdbx/simple/SimpleSaveAndReloadTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.linguafranca.pwdb.kdbx.simple; - -import org.junit.Test; -import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.checks.SaveAndReloadChecks; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxSerializer; - -import java.io.*; - -import static org.junit.Assert.assertTrue; -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class SimpleSaveAndReloadTest extends SaveAndReloadChecks { - - static PrintStream printStream = getTestPrintStream(); - - @Override - public Database getDatabase() { - return new SimpleDatabase(); - } - - @Override - public Database getDatabase(String s, Credentials credentials) throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(s); - try { - return SimpleDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new IOException(e); - } - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - try { - return SimpleDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new IOException(e); - } - } - - - @Override - public boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2) { - KdbxHeader h1 = (KdbxHeader) s1.getStreamConfiguration(); - KdbxHeader h2 = (KdbxHeader) s1.getStreamConfiguration(); - return (h1.getVersion() == h2.getVersion() && - h1.getProtectedStreamAlgorithm().equals(h2.getProtectedStreamAlgorithm()) && - h1.getKeyDerivationFunction().equals(h2.getKeyDerivationFunction()) && - h1.getCipherAlgorithm().equals(h2.getCipherAlgorithm())); - } - - - // check that boolean comes out in upper case - Simple Converters don't work on attributes - // so this is done in the output transformer - @Test - public void uppercaseBooleanTest() throws IOException { - SimpleDatabase s = new SimpleDatabase(); - SimpleEntry e = s.newEntry(); - e.setPassword("12345"); - s.getRootGroup().addEntry(e); - File file = File.createTempFile("kdbx", "kdbx"); - s.save(new KdbxCreds("123".getBytes()), new FileOutputStream(file)); - InputStream inputStream = new FileInputStream(file); - Credentials credentials = new KdbxCreds("123".getBytes()); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); - BufferedReader br = new BufferedReader(new InputStreamReader(decryptedInputStream)); - boolean foundValue = false; - while (br.ready()) { - String string = br.readLine(); - if (string.trim().startsWith(" Date: Fri, 13 Sep 2024 00:12:45 +0100 Subject: [PATCH 02/66] refactor generics remove simple, jaxb and dom and refactor through --- all/pom.xml | 19 +------ database/pom.xml | 2 +- .../java/org/linguafranca/pwdb/Database.java | 30 +++++----- .../java/org/linguafranca/pwdb/Entry.java | 7 ++- .../java/org/linguafranca/pwdb/Group.java | 28 +++++----- .../java/org/linguafranca/pwdb/Visitor.java | 22 ++++---- .../pwdb/base/AbstractDatabase.java | 56 +++++++++---------- .../linguafranca/pwdb/base/AbstractEntry.java | 4 +- .../linguafranca/pwdb/base/AbstractGroup.java | 52 ++++++----------- .../pwdb/protect/ProtectedDatabase.java | 2 +- example/pom.xml | 2 +- .../linguafranca/pwdb/kdbx/OpenDbExample.java | 36 ------------ .../linguafranca/pwdb/kdbx/QuickStart.java | 20 +++---- .../java/org/linguafranca/pwdb/kdbx/Util.java | 14 ++--- .../pwdb/kdbx/SimpleQuickStartTest.java | 21 ++++--- .../pwdb/kdbx/validation/Issue27Test.java | 16 +++--- .../pwdb/kdbx/validation/Issue33Test.java | 21 ------- .../pwdb/kdbx/validation/Issue38Test.java | 10 ++-- jackson/pom.xml | 2 +- .../pwdb/kdbx/jackson/JacksonDatabase.java | 4 +- .../pwdb/kdbx/jackson/JacksonEntry.java | 2 +- .../pwdb/kdbx/jackson/JacksonGroup.java | 5 +- .../jackson/JacksonSerializableDatabase.java | 8 ++- .../pwdb/JacksonPropertyValueTest.java | 2 +- .../pwdb/JacksonSaveAndReloadTest.java | 2 +- kdb/pom.xml | 2 +- .../linguafranca/pwdb/kdb/KdbDatabase.java | 2 +- .../org/linguafranca/pwdb/kdb/KdbEntry.java | 8 +-- .../org/linguafranca/pwdb/kdb/KdbGroup.java | 8 +-- kdbx/pom.xml | 2 +- pom.xml | 2 +- test/pom.xml | 2 +- .../pwdb/checks/BasicDatabaseChecks.java | 12 ++-- .../pwdb/checks/BinaryPropertyChecks.java | 2 +- .../pwdb/checks/DatabaseLoaderChecks.java | 4 +- .../pwdb/checks/PropertyValueChecks.java | 3 +- .../pwdb/checks/RecycleBinChecks.java | 4 +- .../pwdb/checks/SaveAndReloadChecks.java | 2 +- 38 files changed, 175 insertions(+), 265 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index a0748435..d075ffb2 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -19,7 +19,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT ../pom.xml @@ -27,7 +27,7 @@ KeePassJava2 KeePassJava2 :: All - KDB Database support as well as KDBX 3.1: DOM, JAXB and Simple implementations. + KDBX and KDB Database support. @@ -35,21 +35,6 @@ KeePassJava2-kdb ${project.version} - - org.linguafranca.pwdb - KeePassJava2-dom - ${project.version} - - - org.linguafranca.pwdb - KeePassJava2-jaxb - ${project.version} - - - org.linguafranca.pwdb - KeePassJava2-simple - ${project.version} - org.linguafranca.pwdb KeePassJava2-jackson diff --git a/database/pom.xml b/database/pom.xml index f4095dec..1feefcb8 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -3,7 +3,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index 855f92e8..c87fab63 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -49,26 +49,26 @@ * the caller without affecting the underlying database structure, however changes * to the Groups and Entries contained in the lists do modify the database. */ -public interface Database > { +public interface Database , E extends Entry> { /** * get the root group for the database * @return the root group */ - Group getRootGroup(); + G getRootGroup(); /** * Create a new Group * @return the group created */ - Group newGroup(); + G newGroup(); /** * Create a new named Group * @param name the name of the group * @return the group created */ - Group newGroup(String name); + G newGroup(String name); /** * Create a new Group copying the details of the supplied group, but not copying its children @@ -77,19 +77,19 @@ public interface Database > { * @param group the group to copy * @return the group created */ - Group newGroup(Group> group); + G newGroup(Group group); /** * Create a new Entry * @return the entry created */ - Entry newEntry(); + E newEntry(); /** * Create a new Entry with a title * @return the entry created */ - Entry newEntry(String title); + E newEntry(String title); /** * Create a new Entry copying the details of the supplied entry @@ -98,7 +98,7 @@ public interface Database > { * @param entry the entry to copy * @return the entry created */ - Entry newEntry(Entry entry); + E newEntry(Entry entry); /** * Create a new default icon @@ -118,7 +118,7 @@ public interface Database > { * @param uuid the UUID * @return an entry or null if not found */ - @Nullable Entry findEntry(UUID uuid); + @Nullable E findEntry(UUID uuid); /** * Delete an entry with this UUID from anywhere in the database except the recycle bin @@ -133,7 +133,7 @@ public interface Database > { * @param uuid the UUID * @return a group or null if not found */ - @Nullable Group findGroup(UUID uuid); + @Nullable G findGroup(UUID uuid); /** * Delete a group with this UUID from anywhere in the database except the recycle bin @@ -163,7 +163,7 @@ public interface Database > { * or if recycle bin is not supported then return null. * @see #supportsRecycleBin() */ - @Nullable Group getRecycleBin(); + @Nullable G getRecycleBin(); /** * empty the recycle bin whether it is enabled or disabled @@ -177,14 +177,14 @@ public interface Database > { * * @param visitor the visitor to use */ - void visit(Visitor visitor); + void visit(Visitor visitor); /** * Visit all entries starting from a group * @param group the group to start at * @param visitor the visitor to use */ - void visit(Group group, Visitor visitor); + void visit(G group, Visitor visitor); /** * Find all entries that match the criteria @@ -192,7 +192,7 @@ public interface Database > { * @param matcher the matcher to use * @return a list of entries */ - List> findEntries(Entry.Matcher matcher); + List findEntries(Entry.Matcher matcher); /** * Find all entries that match {@link Entry#match(String)} @@ -200,7 +200,7 @@ public interface Database > { * @param find string to find * @return a list of entries */ - List> findEntries(String find); + List findEntries(String find); /** * Gets the name of the database or null if not supported diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 48f2d47f..2b2dbbab 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -43,7 +43,7 @@ * * @author Jo */ -public interface Entry > { +public interface Entry , E extends Entry> { /** * Standard properties are attributes of Entries that are accessible either by * dedicated methods, such as getPassword, or by {@link #getProperty(String)} @@ -67,7 +67,8 @@ public interface Entry > { * method */ interface Matcher { - boolean matches( Entry> entry); + // needs to be raw type so that we can use lambdas + boolean matches(@SuppressWarnings("rawtypes") Entry entry); } /** @@ -213,7 +214,7 @@ interface Matcher { * Get the parent of this entry * @return a parent or null if this entry currently does not have a parent */ - @Nullable Group getParent(); + @Nullable G getParent(); /** * Get the UUID of this entry. Databases (like KDB) that do not natively support diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index faf68395..4ea8f878 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -37,7 +37,7 @@ * * @author Jo */ -public interface Group > { +public interface Group , E extends Entry> { /** * Returns true if this is the root group of a database */ @@ -54,7 +54,7 @@ public interface Group > { * a parent - e.g. if it is newly created or if it has * been removed from a previous parent. */ - @Nullable Group getParent(); + @Nullable G getParent(); /** * Add this group to a parent. The group must be of a type compatible with the database @@ -62,7 +62,7 @@ public interface Group > { * * @param parent a prospective parent */ - void setParent(Group parent); + void setParent(G parent); /** * Returns a list of groups that are the children of this group. @@ -71,7 +71,7 @@ public interface Group > { * * @return a modifiable list of groups */ - List> getGroups(); + List getGroups(); /** * Returns the number of groups that are direct children of this group @@ -90,7 +90,7 @@ public interface Group > { * @param group the group to add * @return the group added */ - Group addGroup(Group group); + G addGroup(G group); /** * Returns a list of child Groups whose name exactly matches that supplied. @@ -100,7 +100,7 @@ public interface Group > { * @param groupName the name of the groups sought * @return a modifiable list */ - List> findGroups(String groupName); + List findGroups(String groupName); /** * Removes the group supplied from this group. The group removed @@ -114,12 +114,12 @@ public interface Group > { * @return the group passed for removal * @throws IllegalArgumentException if the group is not a child of this group */ - Group removeGroup(Group group); + G removeGroup(G group); /** * Returns a modifiable by the caller list of entries contained in this group. */ - List> getEntries(); + List getEntries(); /** * Returns the number of entries in this group @@ -142,7 +142,7 @@ public interface Group > { * @return a modifiable-by-caller list * @see Entry#match(String) */ - List> findEntries(String match, boolean recursive); + List findEntries(String match, boolean recursive); /** * Finds all entries in this group that match using the matcher supplied. @@ -157,7 +157,7 @@ public interface Group > { * @return a modifiable-by-caller list * @see Entry#match(Entry.Matcher) */ - List> findEntries(Entry.Matcher matcher, boolean recursive); + List findEntries(Entry.Matcher matcher, boolean recursive); /** * Adds an entry to this group removing it from another group @@ -165,7 +165,7 @@ public interface Group > { * @param entry the entry to add * @return the entry added */ - Entry addEntry(Entry entry); + E addEntry(E entry); /** * Remove an entry from this group and hence from the database. @@ -173,13 +173,13 @@ public interface Group > { * @return the entry removed * @throws IllegalArgumentException if the Entry is not in the group */ - Entry removeEntry( Entry entry); + E removeEntry(E entry); /** * Make a deep copy of the children a group and add to this group. Does not copy the parent group. * @param parent the group to deep copy */ - void copy(Group parent); + void copy(Group parent); /** * Returns an XPath-like string of the names of Groups from the Root @@ -215,5 +215,5 @@ public interface Group > { /** * Get the database this group was created from */ - @NotNull D getDatabase(); + @NotNull Database getDatabase(); } diff --git a/database/src/main/java/org/linguafranca/pwdb/Visitor.java b/database/src/main/java/org/linguafranca/pwdb/Visitor.java index e66225b6..610e7172 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Visitor.java +++ b/database/src/main/java/org/linguafranca/pwdb/Visitor.java @@ -23,24 +23,24 @@ * * @author jo */ -public interface Visitor > { +public interface Visitor , E extends Entry> { /** * Called on entry to a group visit * @param group the group being visited */ - void startVisit(Group group); + void startVisit(G group); /** * Called on exit from a group visit * @param group the group being exited */ - void endVisit(Group group ); + void endVisit(G group ); /** * Called on visit to an entry * @param entry the entry being visited */ - void visit(Entry entry); + void visit(E entry); /** * called to determine whether to visit entries before subgroups, or not @@ -51,16 +51,16 @@ public interface Visitor > { /** * Empty implementation of Visitor */ - abstract class Default > implements Visitor { + abstract class Default , E extends Entry> implements Visitor { @Override - public void startVisit(Group group) {} + public void startVisit(G group) {} @Override - public void endVisit(Group group) {} + public void endVisit(G group) {} @Override - public void visit(Entry entry) {} + public void visit(E entry) {} @Override public boolean isEntriesFirst() { @@ -71,7 +71,7 @@ public boolean isEntriesFirst() { /** * Visitor prints the Groups and Entries it visits to console */ - class Print > extends Default { + class Print , E extends Entry> extends Default { private final PrintStream printStream; @@ -84,12 +84,12 @@ public Print(PrintStream out) { } @Override - public void startVisit(Group group ) { + public void startVisit(G group ) { printStream.println(group.toString()); } @Override - public void visit(Entry entry) { + public void visit(E entry) { printStream.println(entry.toString()); } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java index e124d8f5..1998f74f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java @@ -27,7 +27,7 @@ * * @author Jo */ -public abstract class AbstractDatabase> implements Database { +public abstract class AbstractDatabase, E extends Entry> implements Database { private boolean isDirty; @@ -41,69 +41,69 @@ public void setDirty(boolean dirty) { } @Override - public void visit(Visitor visitor) { + public void visit(Visitor visitor) { visitor.startVisit(getRootGroup()); visit(getRootGroup(), visitor); visitor.endVisit(getRootGroup()); } @Override - public void visit(Group group, Visitor visitor) { + public void visit(G group, Visitor visitor) { if (visitor.isEntriesFirst()) { - for (Entry entry : group.getEntries()) { + for (E entry : group.getEntries()) { visitor.visit(entry); } } - for (Group g : group.getGroups()) { + for (G g : group.getGroups()) { visitor.startVisit(g); visit(g, visitor); visitor.endVisit(g); } if (!visitor.isEntriesFirst()) { - for (Entry entry : group.getEntries()) { + for (E entry : group.getEntries()) { visitor.visit(entry); } } } @Override - public List> findEntries(Entry.Matcher matcher) { + public List findEntries(Entry.Matcher matcher) { return getRootGroup().findEntries(matcher, true); } @Override - public List> findEntries(String find) { + public List findEntries(String find) { return getRootGroup().findEntries(find, true); } @Override - public Group newGroup(String name) { - Group result = newGroup(); + public G newGroup(String name) { + G result = newGroup(); result.setName(name); return result; } @Override - public Group newGroup(Group group) { - Group result = newGroup(); + public G newGroup(Group group) { + G result = newGroup(); result.setName(group.getName()); result.setIcon(this.newIcon(group.getIcon().getIndex())); return result; } @Override - public Entry newEntry(String title) { - Entry result = newEntry(); + public E newEntry(String title) { + E result = newEntry(); result.setTitle(title); return result; } @Override - public Entry newEntry(Entry entry) { - Entry result = newEntry(); + public E newEntry(Entry entry) { + E result = newEntry(); for (String propertyName: entry.getPropertyNames()) { try { // all implementations must support setting of STANDARD_PROPERTY_NAMES @@ -130,8 +130,8 @@ public Entry newEntry(Entry entry) { } @Override - public Entry findEntry(final UUID uuid) { - List> entries = findEntries(entry -> entry.getUuid().equals(uuid)); + public E findEntry(final UUID uuid) { + List entries = findEntries(entry -> entry.getUuid().equals(uuid)); if (entries.size() > 1) { throw new IllegalStateException("Two entries same UUID"); } @@ -143,7 +143,7 @@ public Entry findEntry(final UUID uuid) { @Override public boolean deleteEntry(final UUID uuid) { - Entry e = findEntry(uuid); + E e = findEntry(uuid); if (e == null) { return false; } @@ -158,13 +158,13 @@ public boolean deleteEntry(final UUID uuid) { } @Override - public Group findGroup(final UUID uuid){ - final List> groups = new ArrayList<>(); - visit(new Visitor.Default() { + public G findGroup(final UUID uuid){ + final List groups = new ArrayList<>(); + visit(new Visitor.Default() { // set to true while visiting sub groups of recycle bin boolean recycle; @Override - public void startVisit(Group group) { + public void startVisit(G group) { if (!recycle && group.getUuid().equals(uuid)) { groups.add(group); } @@ -174,7 +174,7 @@ public void startVisit(Group group) { } @Override - public void endVisit(Group group) { + public void endVisit(G group) { if (group.isRecycleBin()) { recycle = false; } @@ -191,7 +191,7 @@ public void endVisit(Group group) { @Override public boolean deleteGroup(final UUID uuid) { - Group g = findGroup(uuid); + G g = findGroup(uuid); if (g==null) { return false; } @@ -207,14 +207,14 @@ public boolean deleteGroup(final UUID uuid) { @Override public void emptyRecycleBin() { - Group recycle = getRecycleBin(); + G recycle = getRecycleBin(); if (recycle == null) { return; } - for (Group g: recycle.getGroups()){ + for (G g: recycle.getGroups()){ recycle.removeGroup(g); } - for (Entry e: recycle.getEntries()){ + for (E e: recycle.getEntries()){ recycle.removeEntry(e); } } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java index cf4a9f3a..00af076c 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java @@ -23,7 +23,7 @@ * * @author Jo */ -public abstract class AbstractEntry> implements Entry { +public abstract class AbstractEntry, E extends Entry> implements Entry { @Override public boolean matchTitle(String text){ @@ -57,7 +57,7 @@ public boolean match(Entry.Matcher matcher) { @Override public String getPath() { - Group parent = this.getParent(); + G parent = this.getParent(); String result = ""; if (parent != null) { result = parent.getPath(); diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java index 73c8ca41..5f9c045c 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java @@ -29,12 +29,12 @@ * * @author Jo */ -public abstract class AbstractGroup> implements Group { +public abstract class AbstractGroup, E extends Entry> implements Group { @Override - public List> findGroups(String group) { - ArrayList> result = new ArrayList<>(); - for (Group g: getGroups()) { + public List findGroups(String group) { + ArrayList result = new ArrayList<>(); + for (G g: getGroups()) { if (g.getName().equals(group)) { result.add(g); } @@ -43,38 +43,20 @@ public List> findGroups(String group) { } @Override - public List> findEntries(String find, boolean recursive) { - /* - * Local helper class to avoid violating DRY in {@link AbstractGroup#findEntries(String, boolean)}. - * Would-be lambda in Java 8. - */ - class TextMatcher implements Entry.Matcher { - - private final String text; - - private TextMatcher(String text) { - this.text = text; - } - - @Override - public boolean matches(Entry> entry) { - return entry.match(text); - } - } - - return findEntries(new TextMatcher(find), recursive); + public List findEntries(String find, boolean recursive) { + return findEntries(entry -> entry.match(find), recursive); } @Override - public List> findEntries(Entry.Matcher matcher, boolean recursive) { - List > result = new ArrayList<>(getEntries().size()); - for (Entry entry: getEntries()){ + public List findEntries(Entry.Matcher matcher, boolean recursive) { + List result = new ArrayList<>(getEntries().size()); + for (E entry: getEntries()){ if (entry.match(matcher)){ result.add(entry); } } if (recursive) { - for (Group group: getGroups()) { + for (G group: getGroups()) { // don't recurse into recycle bin if (group.isRecycleBin()) { continue; @@ -86,21 +68,21 @@ public List> findEntries(Entry.Matcher matcher, boolean recur } @Override - public void copy(Group parent) { - for (Group child : parent.getGroups()) { - Group addedGroup = addGroup(this.getDatabase().newGroup(child)); + public void copy(Group parent) { + for (Group child : parent.getGroups()) { + G addedGroup = addGroup(this.getDatabase().newGroup(child)); addedGroup.copy(child); } - for (Entry entry : parent.getEntries()) { + for (Entry entry : parent.getEntries()) { addEntry(this.getDatabase().newEntry(entry)); } } @Override public String getPath() { - Stack> parents = new Stack<>(); - Group parent = this; - parents.push(this); + Stack parents = new Stack<>(); + G parent = (G)this; + parents.push((G)this); while ((parent=parent.getParent()) != null) { parents.push(parent); } diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index 7e968f44..964e360c 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -13,7 +13,7 @@ * establishes {@link org.linguafranca.pwdb.PropertyValue.StringStore} storage for unprotected values and * {@link org.linguafranca.pwdb.PropertyValue.SealedStore} for protected values. */ -public abstract class ProtectedDatabase> extends AbstractDatabase { +public abstract class ProtectedDatabase, E extends Entry> extends AbstractDatabase { private PropertyValue.Strategy valueStrategy = new PropertyValue.Strategy.Default(); @Override diff --git a/example/pom.xml b/example/pom.xml index 7cfde215..b0bdc257 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -19,7 +19,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index 8af6c6f3..9b794453 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -2,10 +2,7 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.kdbx.dom.DomDatabaseWrapper; import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; -import org.linguafranca.pwdb.kdbx.jaxb.JaxbDatabase; -import org.linguafranca.pwdb.kdbx.simple.SimpleDatabase; import java.io.InputStream; import java.io.PrintStream; @@ -25,27 +22,6 @@ private interface DbLoader { Database load(KdbxCreds creds, InputStream inputStream) throws Exception; } - private static class SimpleDbLoader implements DbLoader { - @Override - public Database load(KdbxCreds creds, InputStream inputStream) throws Exception { - return SimpleDatabase.load(creds, inputStream); - } - } - - private static class DomDbLoader implements DbLoader { - @Override - public Database load(KdbxCreds creds, InputStream inputStream) throws Exception { - return DomDatabaseWrapper.load(creds, inputStream); - } - } - - private static class JaxbDbLoader implements DbLoader { - @Override - public Database load(KdbxCreds creds, InputStream inputStream) throws Exception { - return JaxbDatabase.load(creds, inputStream); - } - } - private static class JacksonDbLoader implements DbLoader { @Override public Database load(KdbxCreds creds, InputStream inputStream) throws Exception { @@ -71,36 +47,24 @@ public static void testDb (DbLoader loader, String label, int loads, int iterati public static void main(String[] args) throws Exception { printStream.println("Warming up JVM"); - testDb(new SimpleDbLoader(), "Simple", 5, 20); - testDb(new JaxbDbLoader(), "Jaxb", 5, 20); - testDb(new DomDbLoader(), "Dom", 5, 20); testDb(new JacksonDbLoader(), "Jackson", 5, 20); printStream.println("Sleeping"); System.gc(); Thread.sleep(2000); - testDb(new SimpleDbLoader(), "Simple", 5, 20); - testDb(new JaxbDbLoader(), "Jaxb", 5, 20); - testDb(new DomDbLoader(), "Dom", 5, 20); testDb(new JacksonDbLoader(), "Jackson", 5, 20); printStream.println("Sleeping"); System.gc(); Thread.sleep(2000); - testDb(new SimpleDbLoader(), "Simple", 10, 1); - testDb(new JaxbDbLoader(), "Jaxb", 10, 1); - testDb(new DomDbLoader(), "Dom", 10, 1); testDb(new JacksonDbLoader(), "Jackson", 10, 1); printStream.println("Sleeping"); System.gc(); Thread.sleep(2000); - testDb(new SimpleDbLoader(), "Simple", 1, 50); - testDb(new JaxbDbLoader(), "Jaxb", 1, 50); - testDb(new DomDbLoader(), "Dom", 1, 50); testDb(new JacksonDbLoader(), "Jackson", 1, 50); } } diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index c68a93b3..aef1180b 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -20,7 +20,7 @@ import org.linguafranca.pwdb.*; import org.linguafranca.pwdb.kdb.KdbCredentials; import org.linguafranca.pwdb.kdb.KdbDatabase; -import org.linguafranca.pwdb.kdbx.dom.DomDatabaseWrapper; +import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; import org.linguafranca.pwdb.security.Encryption; import java.io.*; @@ -37,7 +37,7 @@ * @author jo */ @SuppressWarnings("WeakerAccess") -public abstract class QuickStart, G extends Group, E extends Entry, I extends Icon> { +public abstract class QuickStart, G extends Group, E extends Entry> { static PrintStream printStream = getTestPrintStream(); @@ -78,11 +78,11 @@ public void saveKdbx() throws IOException { D database = getDatabase(); // add some groups and entries - for (Integer g = 0; g < 5; g++) { - G group = database.getRootGroup().addGroup(database.newGroup(g.toString())); + for (int g = 0; g < 5; g++) { + G group = database.getRootGroup().addGroup(database.newGroup(Integer.toString(g))); for (int e = 0; e <= g; e++) { // entry factory is a local helper to populate an entry - group.addEntry(entryFactory(database, g.toString(), e)); + group.addEntry(entryFactory(database, Integer.toString(g), e)); } } @@ -95,7 +95,7 @@ public void saveKdbx() throws IOException { /** * Splice - add a group from one database to a parent from another (or copy from the same db) */ - public void splice(G newParent, Group groupToSplice) { + public void splice(G newParent, Group groupToSplice) { G addedGroup = newParent.addGroup(newParent.getDatabase().newGroup(groupToSplice)); addedGroup.copy(groupToSplice); } @@ -144,12 +144,12 @@ public void loadKdb() throws IOException { * Load KDBX V3 save as KDBX V4 */ public void loadKdbx3SaveKdbx4(String resourceName, byte[] password, OutputStream v4OutputStream) throws IOException { - DomDatabaseWrapper database; + JacksonDatabase database; // password credentials KdbxCreds credentials = new KdbxCreds(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = DomDatabaseWrapper.load(credentials, inputStream); + database = JacksonDatabase.load(credentials, inputStream); } // create a KDBX (database @@ -176,12 +176,12 @@ public void loadKdbx3SaveKdbx4(String resourceName, byte[] password, OutputStrea * Load KDBX V3 save as KDBX V4 */ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, OutputStream v3OutputStream) throws IOException { - DomDatabaseWrapper database; + JacksonDatabase database; // password credentials KdbxCreds credentials = new KdbxCreds(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = DomDatabaseWrapper.load(credentials, inputStream); + database = JacksonDatabase.load(credentials, inputStream); } // create a KDBX (database diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java index cfffbe96..bec1d689 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java @@ -4,9 +4,7 @@ import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.kdbx.dom.DomDatabaseWrapper; -import org.linguafranca.pwdb.kdbx.jaxb.JaxbDatabase; -import org.linguafranca.pwdb.kdbx.simple.SimpleDatabase; +import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; import java.io.*; import java.nio.charset.StandardCharsets; @@ -16,14 +14,14 @@ public class Util { - List implementations = new ArrayList<>(Arrays.asList(DomDatabaseWrapper.class, SimpleDatabase.class, JaxbDatabase.class)); + List implementations = new ArrayList<>(Arrays.asList(JacksonDatabase.class)); @FunctionalInterface public interface DatabaseLoader { - Database load(Credentials c, InputStream i) throws IOException; + Database load(Credentials c, InputStream i) throws IOException; } - List dbLoader = Arrays.asList(DomDatabaseWrapper::load, SimpleDatabase::load, JaxbDatabase::load); + List dbLoader = Arrays.asList(JacksonDatabase::load); public static InputStream getDecryptedInputStream (String resourceName, Credentials credentials) throws IOException { return getDecryptedInputStream(resourceName, credentials, new KdbxHeader()); @@ -42,7 +40,7 @@ public static String streamToString(InputStream inputStream) throws IOException * Example shows how to list XML from a database (but not decrypted passwords) */ public static void listDatabase(String resourceName, Credentials creds, OutputStream outputStream) throws IOException { - SimpleDatabase database = SimpleDatabase.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); + JacksonDatabase database = JacksonDatabase.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); database.save(new StreamFormat.None(), new KdbxCreds.None(), outputStream); } @@ -50,7 +48,7 @@ public static void listDatabase(String resourceName, Credentials creds, OutputSt * Example shows how to list XML from a database using specified loader */ public static void listDatabase(DatabaseLoader loader, String resourceName, Credentials creds, OutputStream outputStream) throws IOException { - Database database = loader.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); + Database database = loader.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); database.save(new StreamFormat.None(), new KdbxCreds.None(), outputStream); } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index f9c8a71f..b85b145e 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -18,10 +18,9 @@ import org.junit.Test; import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.kdbx.dom.DomDatabaseWrapper; -import org.linguafranca.pwdb.kdbx.dom.DomEntryWrapper; -import org.linguafranca.pwdb.kdbx.dom.DomGroupWrapper; -import org.linguafranca.pwdb.kdbx.dom.DomIconWrapper; +import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; +import org.linguafranca.pwdb.kdbx.jackson.JacksonGroup; import org.linguafranca.pwdb.security.Encryption; import java.io.IOException; @@ -37,18 +36,18 @@ * * @author jo */ -public class SimpleQuickStartTest extends QuickStart { +public class SimpleQuickStartTest extends QuickStart { @Override - public DomDatabaseWrapper getDatabase() { - return new DomDatabaseWrapper(); + public JacksonDatabase getDatabase() { + return new JacksonDatabase(); } @Override - public DomDatabaseWrapper loadDatabase(Credentials credentials, InputStream inputStream){ + public JacksonDatabase loadDatabase(Credentials credentials, InputStream inputStream){ try { - return DomDatabaseWrapper.load(credentials, inputStream); + return JacksonDatabase.load(credentials, inputStream); } catch (Exception e) { throw new IllegalStateException(e); } @@ -76,7 +75,7 @@ public void loadSave() throws IOException { loadKdbx3SaveKdbx4("test123.kdbx","123".getBytes(), Files.newOutputStream(path)); // load newly created V4 database - DomDatabaseWrapper db = DomDatabaseWrapper.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); + JacksonDatabase db = JacksonDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(4, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.CHA_CHA_20, streamFormat.getStreamConfiguration().getCipherAlgorithm()); @@ -90,7 +89,7 @@ public void loadSave2() throws IOException { loadKdbx4SaveKdbx3("V4-ChaCha20-Argon2-Attachment.kdbx","123".getBytes(), Files.newOutputStream(path)); // load newly created V4 database - DomDatabaseWrapper db = DomDatabaseWrapper.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); + JacksonDatabase db = JacksonDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(3, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.AES, streamFormat.getStreamConfiguration().getCipherAlgorithm()); diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java index be04ad11..b0f46328 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java @@ -7,8 +7,8 @@ import org.linguafranca.pwdb.kdbx.KdbxCreds; import org.linguafranca.pwdb.kdbx.KdbxHeader; import org.linguafranca.pwdb.kdbx.KdbxSerializer; -import org.linguafranca.pwdb.kdbx.jaxb.JaxbDatabase; -import org.linguafranca.pwdb.kdbx.jaxb.JaxbEntry; +import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; import java.io.IOException; import java.io.InputStream; @@ -29,10 +29,10 @@ public class Issue27Test { public void testIssue27() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); KdbxCreds creds = new KdbxCreds("passwordless".getBytes()); - JaxbDatabase db = JaxbDatabase.load(creds, is); - List entries = db.findEntries("testtitle"); + JacksonDatabase db = JacksonDatabase.load(creds, is); + List entries = db.findEntries("testtitle"); - for (JaxbEntry entry: entries) { + for (JacksonEntry entry: entries) { printStream.println(Helpers.fromDateV3(entry.getCreationTime())); assertEquals("2021-01-11T09:18:56Z", Helpers.fromDateV3(entry.getCreationTime())); } @@ -45,10 +45,10 @@ public void testIssue27() throws IOException { public void testV4Date() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("V4-AES-AES.kdbx"); KdbxCreds creds = new KdbxCreds("123".getBytes()); - JaxbDatabase db = JaxbDatabase.load(creds, is); - List entries = db.findEntries("Sample Entry #2 - Copy"); + JacksonDatabase db = JacksonDatabase.load(creds, is); + List entries = db.findEntries("Sample Entry #2 - Copy"); - for (JaxbEntry entry: entries) { + for (JacksonEntry entry: entries) { printStream.println(Helpers.fromDate(entry.getCreationTime())); assertEquals("2018-01-26T13:20:58Z", Helpers.fromDateV3(entry.getCreationTime())); } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java index 322ee10a..4a05bed7 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java @@ -9,10 +9,7 @@ import org.linguafranca.pwdb.kdbx.Helpers; import org.linguafranca.pwdb.kdbx.KdbxCreds; import org.linguafranca.pwdb.kdbx.Util; -import org.linguafranca.pwdb.kdbx.dom.DomDatabaseWrapper; import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; -import org.linguafranca.pwdb.kdbx.jaxb.JaxbDatabase; -import org.linguafranca.pwdb.kdbx.simple.SimpleDatabase; import java.io.*; import java.nio.file.Files; @@ -43,24 +40,6 @@ public void refreshInputStream() { inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE); } - @Test - public void testDomDatabaseWrapper() throws IOException { - DomDatabaseWrapper database = DomDatabaseWrapper.load(CREDENTIALS, inputStream); - database.save(new StreamFormat.None(), new Credentials.None(), Files.newOutputStream(Paths.get(TEST_OUTPUT_DIR, "Issue33Dom.xml"))); - } - - @Test - public void testJaxbDatabase() throws IOException { - JaxbDatabase database = JaxbDatabase.load(CREDENTIALS, inputStream); - database.save(new StreamFormat.None(), new Credentials.None(), Files.newOutputStream(Paths.get(TEST_OUTPUT_DIR, "Issue33Jaxb.xml"))); - } - - @Test - public void testSimpleDatabase() throws IOException { - SimpleDatabase database = SimpleDatabase.load(CREDENTIALS, inputStream); - database.save(new StreamFormat.None(), new Credentials.None(), Files.newOutputStream(Paths.get(TEST_OUTPUT_DIR, "Issue33Simple.xml"))); - } - @Test public void testJacksonDatabase() throws IOException { JacksonDatabase database = JacksonDatabase.load(CREDENTIALS, inputStream); diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java index 4684a712..7207922c 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java @@ -2,8 +2,8 @@ import org.junit.Test; import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.dom.DomDatabaseWrapper; -import org.linguafranca.pwdb.kdbx.dom.DomEntryWrapper; +import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; import java.io.IOException; import java.io.InputStream; @@ -25,9 +25,9 @@ public void testV2Keyfile() throws IOException { assert keyStream != null; KdbxCreds creds = new KdbxCreds("MyPassword".getBytes(), keyStream); assert databaseStream != null; - DomDatabaseWrapper database = DomDatabaseWrapper.load(creds, databaseStream); - List entries = database.findEntries("Sample Entry"); - DomEntryWrapper entry = entries.get(0); + JacksonDatabase database = JacksonDatabase.load(creds, databaseStream); + List entries = database.findEntries("Sample Entry"); + JacksonEntry entry = entries.get(0); printStream.println(entry.getTitle()); } } diff --git a/jackson/pom.xml b/jackson/pom.xml index 9726b910..d6e5b535 100644 --- a/jackson/pom.xml +++ b/jackson/pom.xml @@ -18,7 +18,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java index 2b38e66f..9236bdec 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java @@ -34,12 +34,12 @@ import static org.linguafranca.pwdb.kdbx.jackson.JacksonSerializableDatabase.createEmptyDatabase; -public class JacksonDatabase extends ProtectedDatabase { +public class JacksonDatabase extends ProtectedDatabase { KeePassFile keePassFile; StreamFormat streamFormat; - public JacksonDatabase() throws IOException { + public JacksonDatabase() { this(createEmptyDatabase(), null); } diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java index 6755f7be..ec35567a 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java @@ -65,7 +65,7 @@ @JsonIgnoreProperties({"path", "username", "title", "notes", "url", "password"}) -public class JacksonEntry extends AbstractEntry { +public class JacksonEntry extends AbstractEntry { @JacksonXmlProperty(localName = "UUID") diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java index 5076aa13..8ec0edb9 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.base.AbstractGroup; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; @@ -54,7 +53,7 @@ "group", }) @JsonIgnoreProperties(ignoreUnknown=true) -public class JacksonGroup extends AbstractGroup { +public class JacksonGroup extends AbstractGroup{ @JacksonXmlProperty(localName = "UUID") @JsonDeserialize(converter = Base64ToUUIDConverter.class) @@ -116,7 +115,7 @@ public class JacksonGroup extends AbstractGroup { @JsonIgnore protected JacksonGroup parent; - protected JacksonGroup() { + public JacksonGroup() { entries = new ArrayList<>(); groups = new ArrayList<>(); times = new Times(); diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java index a3f9d10a..694b001b 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java @@ -45,12 +45,16 @@ public class JacksonSerializableDatabase implements SerializableDatabase { private PropertyValue.Strategy propertyValueStrategy = new PropertyValue.Strategy.Default(); - public static KeePassFile createEmptyDatabase() throws IOException { + public static KeePassFile createEmptyDatabase() { InputStream inputStream = JacksonSerializableDatabase.class.getClassLoader() .getResourceAsStream("base.kdbx.xml"); XmlMapper mapper = new XmlMapper(); - return mapper.readValue(inputStream, KeePassFile.class); + try { + return mapper.readValue(inputStream, KeePassFile.class); + } catch (IOException e) { + throw new RuntimeException(e); + } } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java b/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java index 40571e53..ac60d0e0 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java +++ b/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java @@ -28,7 +28,7 @@ import java.io.InputStream; import java.io.OutputStream; -public class JacksonPropertyValueTest extends PropertyValueChecks { +public class JacksonPropertyValueTest extends PropertyValueChecks { public JacksonPropertyValueTest() throws IOException { super(true); diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java b/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java index fcb2db76..06c74ab8 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java +++ b/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java @@ -39,7 +39,7 @@ import org.linguafranca.pwdb.kdbx.jackson.JacksonIcon; -public class JacksonSaveAndReloadTest extends SaveAndReloadChecks{ +public class JacksonSaveAndReloadTest extends SaveAndReloadChecks{ @Override public JacksonDatabase getDatabase() { diff --git a/kdb/pom.xml b/kdb/pom.xml index f78d55f9..46f163c3 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -3,7 +3,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java index fbb8634e..93d062b8 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java @@ -30,7 +30,7 @@ * * @author jo */ -public class KdbDatabase extends AbstractDatabase { +public class KdbDatabase extends AbstractDatabase { private String description; private final KdbGroup rootGroup; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index 15c32115..e7716d00 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -32,13 +32,13 @@ * * @author jo */ -public class KdbEntry extends AbstractEntry { +public class KdbEntry extends AbstractEntry { KdbGroup parent; private UUID uuid = UUID.randomUUID(); private String title = ""; private String url = ""; private String notes = ""; - private KdbIcon icon = new KdbIcon(0); + private Icon icon = new KdbIcon(0); private String username = ""; private String password = ""; private Date creationTime = new Date((System.currentTimeMillis())); // to the next lower second @@ -153,12 +153,12 @@ public void setNotes(String notes) { } @Override - public KdbIcon getIcon() { + public Icon getIcon() { return icon; } @Override - public void setIcon(KdbIcon icon) { + public void setIcon(Icon icon) { this.icon = icon; } diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index 1810cb63..b027ca10 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -32,13 +32,13 @@ * The class holds a KDB Group * @author jo */ -public class KdbGroup extends AbstractGroup { +public class KdbGroup extends AbstractGroup { private boolean root; private KdbGroup parent; protected KdbDatabase database; private UUID uuid = UUID.randomUUID(); private String name = ""; - private KdbIcon icon = new KdbIcon(0); + private Icon icon = new KdbIcon(0); private List groups = new ArrayList<>(); private List entries = new ArrayList<>(); private Date creationTime; @@ -158,12 +158,12 @@ void setUuid(UUID uuid) { } @Override - public KdbIcon getIcon() { + public Icon getIcon() { return icon; } @Override - public void setIcon(KdbIcon icon) { + public void setIcon(Icon icon) { this.icon = icon; } diff --git a/kdbx/pom.xml b/kdbx/pom.xml index 282eeed4..414f71bb 100644 --- a/kdbx/pom.xml +++ b/kdbx/pom.xml @@ -19,7 +19,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT ../pom.xml 4.0.0 diff --git a/pom.xml b/pom.xml index bab30c91..9587873e 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.linguafranca.pwdb KeePassJava2-parent - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT database test diff --git a/test/pom.xml b/test/pom.xml index 529ff049..07875d5e 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -3,7 +3,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.2.3-SNAPSHOT + 2.3-SNAPSHOT 4.0.0 diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java index 02764f41..f606d5c2 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java @@ -34,11 +34,11 @@ /** * @author Jo */ -public abstract class BasicDatabaseChecks , G extends Group, E extends Entry, I extends Icon> { +public abstract class BasicDatabaseChecks , E extends Entry> { - protected Database database; + protected Database database; - public abstract Database createDatabase() throws IOException; + public abstract Database createDatabase() throws IOException; public BasicDatabaseChecks() throws IOException { this.database = createDatabase(); @@ -75,7 +75,7 @@ public void testAddGroup() { @Test public void testDeleteGroup () { - Group g1 = database.getRootGroup().addGroup(database.newGroup("group1")); + Group g1 = database.getRootGroup().addGroup(database.newGroup("group1")); List l1 = database.getRootGroup().findGroups("group1"); assertEquals(1, l1.size()); G g2 = l1.get(0); @@ -130,7 +130,7 @@ public void testSetFields () { Assert.assertTrue(e1.matchTitle("1")); Assert.assertFalse(e1.matchTitle("doggy")); - I ic1 = database.newIcon(27); + Icon ic1 = database.newIcon(27); e1.setIcon(ic1); assertEquals(e1.getIcon(), ic1); @@ -244,7 +244,7 @@ public void testCopy() throws IOException { entry1.setIcon(database.newIcon(2)); // create a new Database - Database database2 = createDatabase(); + Database database2 = createDatabase(); // create a new Entry in new Database E entry2 = database2.newEntry(entry1); diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java index 92998e9b..206d9309 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java @@ -48,7 +48,7 @@ * * @author jo */ -public abstract class BinaryPropertyChecks , G extends Group, E extends Entry, I extends Icon> { +public abstract class BinaryPropertyChecks , G extends Group, E extends Entry> { public D database; diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java index 140ea1c1..3f7b7642 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java @@ -32,11 +32,11 @@ /** * @author jo */ -public abstract class DatabaseLoaderChecks , G extends Group, E extends Entry, I extends Icon>{ +public abstract class DatabaseLoaderChecks , G extends Group, E extends Entry> { static PrintStream printStream = getTestPrintStream(); - protected Database database; + protected Database database; protected boolean skipDateCheck = false; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssX"); /** diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java index ec9a153a..503244d4 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java @@ -32,8 +32,7 @@ /** * Testing the operation of PropertyValue mechanisms */ -public abstract class PropertyValueChecks, G extends Group, - E extends Entry, I extends Icon> { +public abstract class PropertyValueChecks, G extends Group, E extends Entry> { public abstract void saveDatabase(D database, Credentials credentials, OutputStream outputStream) throws IOException; public abstract D loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java index 3fb007ae..10caec9a 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java @@ -11,9 +11,9 @@ /** * @author jo */ -public abstract class RecycleBinChecks , G extends Group, E extends Entry, I extends Icon> { +public abstract class RecycleBinChecks , G extends Group, E extends Entry> { - protected Database database; + protected Database database; @Test public void recycleBinEntries() { diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java index 4e45886b..dcd6e895 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java @@ -33,7 +33,7 @@ /** * @author jo */ -public abstract class SaveAndReloadChecks , G extends Group, E extends Entry, I extends Icon>{ +public abstract class SaveAndReloadChecks , G extends Group, E extends Entry> { static PrintStream printStream = getTestPrintStream(); From a9d6eec7ed21c572f940bb2b65861dd07b29449f Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 13 Sep 2024 12:28:13 +0100 Subject: [PATCH 03/66] SecureCharBufferPropertyValue example and refactor tests to suit --- .../linguafranca/util/PropertyValueUtil.java | 59 ++++++++++++ .../linguafranca/pwdb/PropertyValueTest.java | 89 +------------------ .../SecureCharBufferPropertyValue.java | 19 ++-- .../SecureCharBufferPropertyValueTest.java | 15 ++++ 4 files changed, 91 insertions(+), 91 deletions(-) create mode 100644 database/src/main/java/org/linguafranca/util/PropertyValueUtil.java create mode 100644 example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java diff --git a/database/src/main/java/org/linguafranca/util/PropertyValueUtil.java b/database/src/main/java/org/linguafranca/util/PropertyValueUtil.java new file mode 100644 index 00000000..7eb17456 --- /dev/null +++ b/database/src/main/java/org/linguafranca/util/PropertyValueUtil.java @@ -0,0 +1,59 @@ +package org.linguafranca.util; + +import org.linguafranca.pwdb.PropertyValue; + +import java.nio.charset.StandardCharsets; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * Make available as a utility to provide for PropertyValue extension implementations + */ +public class PropertyValueUtil { + + public static final String THIS_IS_A_SECRET = "This is a secret + לַחַיִּים"; + + public static void genericTest(PropertyValue.Factory factory) { + genericTest(factory, true); + } + + public static void genericTest(PropertyValue.Factory factory, boolean testStringReturn) { + // test input CharSequence + PropertyValue testValue = factory.of(THIS_IS_A_SECRET); + test(testStringReturn, testValue); + + // test input char[] + testValue = factory.of(THIS_IS_A_SECRET.toCharArray()); + test(testStringReturn, testValue); + + + // test input byte[] + testValue = factory.of(THIS_IS_A_SECRET.getBytes()); + test(testStringReturn, testValue); + + } + + private static void test(boolean testStringReturn, PropertyValue testValue) { + if (testStringReturn) assertEquals(THIS_IS_A_SECRET, testValue.getValue().toString()); + assertEquals(THIS_IS_A_SECRET, testValue.getValueAsString()); + + // get value as chars + char[] chars = testValue.getValueAsChars(); + // return value is correct + assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), chars); + chars[0] = 'z'; + // return value is still correct + assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), testValue.getValueAsChars()); + + + // get value as bytes + byte[] bytes = testValue.getValueAsBytes(); + // return value is correct + assertArrayEquals(THIS_IS_A_SECRET.getBytes(StandardCharsets.UTF_8), bytes); + bytes[0] = 'z'; + // return value is still correct + assertArrayEquals(THIS_IS_A_SECRET.getBytes(StandardCharsets.UTF_8), testValue.getValueAsBytes()); + } + +} diff --git a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java index 21918401..d8c9cffe 100644 --- a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java @@ -7,80 +7,24 @@ import java.nio.charset.StandardCharsets; import static org.junit.Assert.*; +import static org.linguafranca.util.PropertyValueUtil.genericTest; public class PropertyValueTest { - public static final String THIS_IS_A_SECRET = "This is a secret + לַחַיִּים"; public static final String ANOTHER_SECRET = "password with accents àéç"; Logger logger = LoggerFactory.getLogger(PropertyValueTest.class); @Test public void bytesTest() { - PropertyValue.BytesStore.Factory factory = PropertyValue.BytesStore.getFactory(); - - // test as CharSequence - PropertyValue.BytesStore testValue = factory.of(THIS_IS_A_SECRET); - assertEquals(THIS_IS_A_SECRET, testValue.getValue().toString()); - assertEquals(THIS_IS_A_SECRET, testValue.getValueAsString()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), testValue.getValueAsChars()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(StandardCharsets.UTF_8), testValue.getValueAsBytes()); - // test as char[] - testValue = factory.of(THIS_IS_A_SECRET.toCharArray()); - assertEquals(THIS_IS_A_SECRET, testValue.getValue().toString()); - assertEquals(THIS_IS_A_SECRET, testValue.getValueAsString()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), testValue.getValueAsChars()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(StandardCharsets.UTF_8), testValue.getValueAsBytes()); - // test as byte[] - testValue = factory.of(THIS_IS_A_SECRET.getBytes()); - assertEquals(THIS_IS_A_SECRET, testValue.getValue().toString()); - assertEquals(THIS_IS_A_SECRET, testValue.getValueAsString()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), testValue.getValueAsChars()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(StandardCharsets.UTF_8), testValue.getValueAsBytes()); - } + genericTest(PropertyValue.BytesStore.getFactory());} @Test public void sealedObjectTest() { - PropertyValue.SealedStore.Factory factory = PropertyValue.SealedStore.getFactory(); - - PropertyValue.SealedStore sealed = factory.of(THIS_IS_A_SECRET); - assertEquals(THIS_IS_A_SECRET, sealed.getValue().toString()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(), sealed.getValueAsBytes()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), sealed.getValueAsChars()); - assertEquals(THIS_IS_A_SECRET, sealed.getValueAsString()); - - sealed = factory.of(THIS_IS_A_SECRET.getBytes()); - assertEquals(THIS_IS_A_SECRET, sealed.getValue().toString()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(), sealed.getValueAsBytes()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), sealed.getValueAsChars()); - assertEquals(THIS_IS_A_SECRET, sealed.getValueAsString()); - - sealed =factory.of(THIS_IS_A_SECRET.toCharArray()); - assertEquals(THIS_IS_A_SECRET, sealed.getValue().toString()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(), sealed.getValueAsBytes()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), sealed.getValueAsChars()); - assertEquals(THIS_IS_A_SECRET, sealed.getValueAsString()); + genericTest(PropertyValue.SealedStore.getFactory()); } @Test public void stringTest() { - PropertyValue.Factory factory = PropertyValue.StringStore.getFactory(); - - PropertyValue.StringStore stringStore = factory.of(THIS_IS_A_SECRET); - assertEquals(THIS_IS_A_SECRET, stringStore.getValue()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(), stringStore.getValueAsBytes()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), stringStore.getValueAsChars()); - assertEquals(THIS_IS_A_SECRET, stringStore.getValueAsString()); - - stringStore = factory.of(THIS_IS_A_SECRET.getBytes()); - assertEquals(THIS_IS_A_SECRET, stringStore.getValue()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(), stringStore.getValueAsBytes()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), stringStore.getValueAsChars()); - assertEquals(THIS_IS_A_SECRET, stringStore.getValueAsString()); - - stringStore =factory.of(THIS_IS_A_SECRET.toCharArray()); - assertEquals(THIS_IS_A_SECRET, stringStore.getValue()); - assertArrayEquals(THIS_IS_A_SECRET.getBytes(), stringStore.getValueAsBytes()); - assertArrayEquals(THIS_IS_A_SECRET.toCharArray(), stringStore.getValueAsChars()); - assertEquals(THIS_IS_A_SECRET, stringStore.getValueAsString()); + genericTest(PropertyValue.StringStore.getFactory()); } @Test @@ -90,29 +34,4 @@ public void sealedScriptTest(){ assertArrayEquals(ANOTHER_SECRET.getBytes(StandardCharsets.UTF_8), bytes); } - - @Test - public void getCharsTest(){ - PropertyValue.BytesStore cs = new PropertyValue.BytesStore("a test".toCharArray()); - char [] value = cs.getValueAsChars(); - value[0] = 'b'; - // changing the retrieved copy doesn't change the source - assertArrayEquals("a test".toCharArray(), cs.getValueAsChars()); - - byte [] bytes = cs.getValueAsBytes(); - bytes [0] = 0; - // same for bytes - assertArrayEquals("a test".getBytes(), cs.getValueAsBytes()); - } - - @Test - public void putCharsTest(){ - String testValue = "a test"; - char [] chars = testValue.toCharArray(); - PropertyValue.BytesStore cs = new PropertyValue.BytesStore(chars); - chars[0] = 'b'; - char [] value = cs.getValueAsChars(); - // changing the source doesn't change the copy - assertArrayEquals(testValue.toCharArray(), cs.getValueAsChars()); - } } \ No newline at end of file diff --git a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java index 0b84d4ae..e016215a 100644 --- a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java +++ b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java @@ -16,20 +16,20 @@ public class SecureCharBufferPropertyValue implements PropertyValue { /** * Builder for {@link SecureCharBufferPropertyValue} */ - static class Factory implements PropertyValue.Factory { + static class Factory implements PropertyValue.Factory { @Override - public PropertyValue of(CharSequence aString) { + public SecureCharBufferPropertyValue of(CharSequence aString) { return new SecureCharBufferPropertyValue(aString); } @Override - public PropertyValue of(char[] value) { + public SecureCharBufferPropertyValue of(char[] value) { return new SecureCharBufferPropertyValue(value); } @Override - public PropertyValue of(byte[] value) { + public SecureCharBufferPropertyValue of(byte[] value) { return new SecureCharBufferPropertyValue(value); } } @@ -60,12 +60,19 @@ public CharSequence getValue() { @Override public char [] getValueAsChars() { - return CharBuffer.wrap(value).array(); + CharBuffer cb = CharBuffer.wrap(value); + char[] result = new char[cb.limit()]; + cb.get(result); + return result; } @Override public byte [] getValueAsBytes() { - return StandardCharsets.UTF_8.encode(CharBuffer.wrap(value)).array(); + CharBuffer cb = CharBuffer.wrap(value); + ByteBuffer bb = StandardCharsets.UTF_8.encode(cb); + byte[] result = new byte[bb.limit()]; + bb.get(result); + return result; } @Override diff --git a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java new file mode 100644 index 00000000..0f1e2c5d --- /dev/null +++ b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java @@ -0,0 +1,15 @@ +package org.linguafranca.pwdb.example; + +import org.junit.Test; + +import static org.junit.Assert.*; +import static org.linguafranca.util.PropertyValueUtil.genericTest; + +public class SecureCharBufferPropertyValueTest { + + @Test + public void storageTest(){ + genericTest(new SecureCharBufferPropertyValue.Factory(), false); + } + +} \ No newline at end of file From 7b3bf7110da70fc4d717a43074fb3ea9cec8dc18 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 13 Sep 2024 15:35:49 +0100 Subject: [PATCH 04/66] removed generics from database classes --- .../java/org/linguafranca/pwdb/Database.java | 32 +++++----- .../java/org/linguafranca/pwdb/Entry.java | 6 +- .../java/org/linguafranca/pwdb/Group.java | 28 ++++----- .../java/org/linguafranca/pwdb/Visitor.java | 22 +++---- .../pwdb/base/AbstractDatabase.java | 56 +++++++++--------- .../linguafranca/pwdb/base/AbstractEntry.java | 4 +- .../linguafranca/pwdb/base/AbstractGroup.java | 32 +++++----- .../pwdb/protect/ProtectedDatabase.java | 2 +- .../linguafranca/pwdb/kdbx/QuickStart.java | 40 ++++++------- .../java/org/linguafranca/pwdb/kdbx/Util.java | 4 +- .../pwdb/kdbx/SimpleQuickStartTest.java | 2 +- .../pwdb/kdbx/validation/Issue27Test.java | 9 +-- .../pwdb/kdbx/validation/Issue38Test.java | 5 +- .../pwdb/kdbx/jackson/JacksonDatabase.java | 6 +- .../pwdb/kdbx/jackson/JacksonEntry.java | 52 ++++++++--------- .../pwdb/kdbx/jackson/JacksonGroup.java | 19 ++++-- .../pwdb/JacksonPropertyValueTest.java | 4 +- .../pwdb/JacksonSaveAndReloadTest.java | 4 +- .../linguafranca/pwdb/kdb/KdbDatabase.java | 2 +- .../org/linguafranca/pwdb/kdb/KdbEntry.java | 2 +- .../org/linguafranca/pwdb/kdb/KdbGroup.java | 17 ++++-- .../pwdb/checks/BasicDatabaseChecks.java | 46 +++++++-------- .../pwdb/checks/BinaryPropertyChecks.java | 34 +++++------ .../pwdb/checks/DatabaseLoaderChecks.java | 12 ++-- .../pwdb/checks/PropertyValueChecks.java | 18 +++--- .../pwdb/checks/RecycleBinChecks.java | 12 ++-- .../pwdb/checks/SaveAndReloadChecks.java | 58 +++++++++---------- 27 files changed, 271 insertions(+), 257 deletions(-) diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index c87fab63..c1a4d387 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -49,26 +49,26 @@ * the caller without affecting the underlying database structure, however changes * to the Groups and Entries contained in the lists do modify the database. */ -public interface Database , E extends Entry> { +public interface Database { /** * get the root group for the database * @return the root group */ - G getRootGroup(); + Group getRootGroup(); /** * Create a new Group * @return the group created */ - G newGroup(); + Group newGroup(); /** * Create a new named Group * @param name the name of the group * @return the group created */ - G newGroup(String name); + Group newGroup(String name); /** * Create a new Group copying the details of the supplied group, but not copying its children @@ -77,19 +77,19 @@ public interface Database , E extends Entry> { * @param group the group to copy * @return the group created */ - G newGroup(Group group); + Group newGroup(Group group); /** * Create a new Entry * @return the entry created */ - E newEntry(); + Entry newEntry(); /** * Create a new Entry with a title * @return the entry created */ - E newEntry(String title); + Entry newEntry(String title); /** * Create a new Entry copying the details of the supplied entry @@ -98,7 +98,7 @@ public interface Database , E extends Entry> { * @param entry the entry to copy * @return the entry created */ - E newEntry(Entry entry); + Entry newEntry(Entry entry); /** * Create a new default icon @@ -118,7 +118,7 @@ public interface Database , E extends Entry> { * @param uuid the UUID * @return an entry or null if not found */ - @Nullable E findEntry(UUID uuid); + @Nullable Entry findEntry(UUID uuid); /** * Delete an entry with this UUID from anywhere in the database except the recycle bin @@ -133,7 +133,7 @@ public interface Database , E extends Entry> { * @param uuid the UUID * @return a group or null if not found */ - @Nullable G findGroup(UUID uuid); + @Nullable Group findGroup(UUID uuid); /** * Delete a group with this UUID from anywhere in the database except the recycle bin @@ -163,7 +163,7 @@ public interface Database , E extends Entry> { * or if recycle bin is not supported then return null. * @see #supportsRecycleBin() */ - @Nullable G getRecycleBin(); + @Nullable Group getRecycleBin(); /** * empty the recycle bin whether it is enabled or disabled @@ -177,14 +177,14 @@ public interface Database , E extends Entry> { * * @param visitor the visitor to use */ - void visit(Visitor visitor); + void visit(Visitor visitor); /** * Visit all entries starting from a group * @param group the group to start at * @param visitor the visitor to use */ - void visit(G group, Visitor visitor); + void visit(Group group, Visitor visitor); /** * Find all entries that match the criteria @@ -192,7 +192,7 @@ public interface Database , E extends Entry> { * @param matcher the matcher to use * @return a list of entries */ - List findEntries(Entry.Matcher matcher); + List findEntries(Entry.Matcher matcher); /** * Find all entries that match {@link Entry#match(String)} @@ -200,7 +200,7 @@ public interface Database , E extends Entry> { * @param find string to find * @return a list of entries */ - List findEntries(String find); + List findEntries(String find); /** * Gets the name of the database or null if not supported @@ -269,7 +269,7 @@ public interface Database , E extends Entry> { /** * Get the default means of storage of unprotected and protected property values */ - public PropertyValue.Strategy getPropertyValueStrategy(); + PropertyValue.Strategy getPropertyValueStrategy(); /** * Set the default means of storage of unprotected and protected property values diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 32d42026..8b743b11 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -43,7 +43,7 @@ * * @author Jo */ -public interface Entry , E extends Entry> { +public interface Entry { /** * Standard properties are attributes of Entries that are accessible either by * dedicated methods, such as getPassword, or by {@link #getProperty(String)} @@ -68,7 +68,7 @@ public interface Entry , E extends Entry> { */ interface Matcher { // needs to be raw type so that we can use lambdas - boolean matches(@SuppressWarnings("rawtypes") Entry entry); + boolean matches(Entry entry); } /** @@ -214,7 +214,7 @@ interface Matcher { * Get the parent of this entry * @return a parent or null if this entry currently does not have a parent */ - @Nullable G getParent(); + @Nullable Group getParent(); /** * Get the UUID of this entry. Databases (like KDB) that do not natively support diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index 4ea8f878..9696014f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -37,7 +37,7 @@ * * @author Jo */ -public interface Group , E extends Entry> { +public interface Group { /** * Returns true if this is the root group of a database */ @@ -54,7 +54,7 @@ public interface Group , E extends Entry> { * a parent - e.g. if it is newly created or if it has * been removed from a previous parent. */ - @Nullable G getParent(); + @Nullable Group getParent(); /** * Add this group to a parent. The group must be of a type compatible with the database @@ -62,7 +62,7 @@ public interface Group , E extends Entry> { * * @param parent a prospective parent */ - void setParent(G parent); + void setParent(Group parent); /** * Returns a list of groups that are the children of this group. @@ -71,7 +71,7 @@ public interface Group , E extends Entry> { * * @return a modifiable list of groups */ - List getGroups(); + List getGroups(); /** * Returns the number of groups that are direct children of this group @@ -90,7 +90,7 @@ public interface Group , E extends Entry> { * @param group the group to add * @return the group added */ - G addGroup(G group); + Group addGroup(Group group); /** * Returns a list of child Groups whose name exactly matches that supplied. @@ -100,7 +100,7 @@ public interface Group , E extends Entry> { * @param groupName the name of the groups sought * @return a modifiable list */ - List findGroups(String groupName); + List findGroups(String groupName); /** * Removes the group supplied from this group. The group removed @@ -114,12 +114,12 @@ public interface Group , E extends Entry> { * @return the group passed for removal * @throws IllegalArgumentException if the group is not a child of this group */ - G removeGroup(G group); + Group removeGroup(Group group); /** * Returns a modifiable by the caller list of entries contained in this group. */ - List getEntries(); + List getEntries(); /** * Returns the number of entries in this group @@ -142,7 +142,7 @@ public interface Group , E extends Entry> { * @return a modifiable-by-caller list * @see Entry#match(String) */ - List findEntries(String match, boolean recursive); + List findEntries(String match, boolean recursive); /** * Finds all entries in this group that match using the matcher supplied. @@ -157,7 +157,7 @@ public interface Group , E extends Entry> { * @return a modifiable-by-caller list * @see Entry#match(Entry.Matcher) */ - List findEntries(Entry.Matcher matcher, boolean recursive); + List findEntries(Entry.Matcher matcher, boolean recursive); /** * Adds an entry to this group removing it from another group @@ -165,7 +165,7 @@ public interface Group , E extends Entry> { * @param entry the entry to add * @return the entry added */ - E addEntry(E entry); + Entry addEntry(Entry entry); /** * Remove an entry from this group and hence from the database. @@ -173,13 +173,13 @@ public interface Group , E extends Entry> { * @return the entry removed * @throws IllegalArgumentException if the Entry is not in the group */ - E removeEntry(E entry); + Entry removeEntry(Entry entry); /** * Make a deep copy of the children a group and add to this group. Does not copy the parent group. * @param parent the group to deep copy */ - void copy(Group parent); + void copy(Group parent); /** * Returns an XPath-like string of the names of Groups from the Root @@ -215,5 +215,5 @@ public interface Group , E extends Entry> { /** * Get the database this group was created from */ - @NotNull Database getDatabase(); + @NotNull Database getDatabase(); } diff --git a/database/src/main/java/org/linguafranca/pwdb/Visitor.java b/database/src/main/java/org/linguafranca/pwdb/Visitor.java index 610e7172..e7e60e65 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Visitor.java +++ b/database/src/main/java/org/linguafranca/pwdb/Visitor.java @@ -23,24 +23,24 @@ * * @author jo */ -public interface Visitor , E extends Entry> { +public interface Visitor { /** * Called on entry to a group visit * @param group the group being visited */ - void startVisit(G group); + void startVisit(Group group); /** * Called on exit from a group visit * @param group the group being exited */ - void endVisit(G group ); + void endVisit(Group group ); /** * Called on visit to an entry * @param entry the entry being visited */ - void visit(E entry); + void visit(Entry entry); /** * called to determine whether to visit entries before subgroups, or not @@ -51,16 +51,16 @@ public interface Visitor , E extends Entry> { /** * Empty implementation of Visitor */ - abstract class Default , E extends Entry> implements Visitor { + abstract class Default implements Visitor { @Override - public void startVisit(G group) {} + public void startVisit(Group group) {} @Override - public void endVisit(G group) {} + public void endVisit(Group group) {} @Override - public void visit(E entry) {} + public void visit(Entry entry) {} @Override public boolean isEntriesFirst() { @@ -71,7 +71,7 @@ public boolean isEntriesFirst() { /** * Visitor prints the Groups and Entries it visits to console */ - class Print , E extends Entry> extends Default { + class Print extends Default{ private final PrintStream printStream; @@ -84,12 +84,12 @@ public Print(PrintStream out) { } @Override - public void startVisit(G group ) { + public void startVisit(Group group ) { printStream.println(group.toString()); } @Override - public void visit(E entry) { + public void visit(Entry entry) { printStream.println(entry.toString()); } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java index 9b4eb077..4e8a5608 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java @@ -27,7 +27,7 @@ * * @author Jo */ -public abstract class AbstractDatabase, E extends Entry> implements Database { +public abstract class AbstractDatabase implements Database { private boolean isDirty; @@ -41,69 +41,69 @@ public void setDirty(boolean dirty) { } @Override - public void visit(Visitor visitor) { + public void visit(Visitor visitor) { visitor.startVisit(getRootGroup()); visit(getRootGroup(), visitor); visitor.endVisit(getRootGroup()); } @Override - public void visit(G group, Visitor visitor) { + public void visit(Group group, Visitor visitor) { if (visitor.isEntriesFirst()) { - for (E entry : group.getEntries()) { + for (Entry entry : group.getEntries()) { visitor.visit(entry); } } - for (G g : group.getGroups()) { + for (Group g : group.getGroups()) { visitor.startVisit(g); visit(g, visitor); visitor.endVisit(g); } if (!visitor.isEntriesFirst()) { - for (E entry : group.getEntries()) { + for (Entry entry : group.getEntries()) { visitor.visit(entry); } } } @Override - public List findEntries(Entry.Matcher matcher) { + public List findEntries(Entry.Matcher matcher) { return getRootGroup().findEntries(matcher, true); } @Override - public List findEntries(String find) { + public List findEntries(String find) { return getRootGroup().findEntries(find, true); } @Override - public G newGroup(String name) { - G result = newGroup(); + public Group newGroup(String name) { + Group result = newGroup(); result.setName(name); return result; } @Override - public G newGroup(Group group) { - G result = newGroup(); + public Group newGroup(Group group) { + Group result = newGroup(); result.setName(group.getName()); result.setIcon(this.newIcon(group.getIcon().getIndex())); return result; } @Override - public E newEntry(String title) { - E result = newEntry(); + public Entry newEntry(String title) { + Entry result = newEntry(); result.setTitle(title); return result; } @Override - public E newEntry(Entry entry) { - E result = newEntry(); + public Entry newEntry(Entry entry) { + Entry result = newEntry(); for (String propertyName: entry.getPropertyNames()) { try { // all implementations must support setting of STANDARD_PROPERTY_NAMES @@ -130,8 +130,8 @@ public E newEntry(Entry entry) { } @Override - public E findEntry(final UUID uuid) { - List entries = findEntries(entry -> entry.getUuid().equals(uuid)); + public Entry findEntry(final UUID uuid) { + List entries = findEntries(entry -> entry.getUuid().equals(uuid)); if (entries.size() > 1) { throw new IllegalStateException("Two entries same UUID"); } @@ -143,7 +143,7 @@ public E findEntry(final UUID uuid) { @Override public boolean deleteEntry(final UUID uuid) { - E e = findEntry(uuid); + Entry e = findEntry(uuid); if (e == null) { return false; } @@ -158,13 +158,13 @@ public boolean deleteEntry(final UUID uuid) { } @Override - public G findGroup(final UUID uuid){ - final List groups = new ArrayList<>(); - visit(new Visitor.Default() { + public Group findGroup(final UUID uuid){ + final List groups = new ArrayList<>(); + visit(new Visitor.Default() { // set to true while visiting sub groups of recycle bin boolean recycle; @Override - public void startVisit(G group) { + public void startVisit(Group group) { if (!recycle && group.getUuid().equals(uuid)) { groups.add(group); } @@ -174,7 +174,7 @@ public void startVisit(G group) { } @Override - public void endVisit(G group) { + public void endVisit(Group group) { if (group.isRecycleBin()) { recycle = false; } @@ -191,7 +191,7 @@ public void endVisit(G group) { @Override public boolean deleteGroup(final UUID uuid) { - G g = findGroup(uuid); + Group g = findGroup(uuid); if (g==null) { return false; } @@ -207,14 +207,14 @@ public boolean deleteGroup(final UUID uuid) { @Override public void emptyRecycleBin() { - G recycle = getRecycleBin(); + Group recycle = getRecycleBin(); if (recycle == null) { return; } - for (G g: recycle.getGroups()){ + for (Group g: recycle.getGroups()){ recycle.removeGroup(g); } - for (E e: recycle.getEntries()){ + for (Entry e: recycle.getEntries()){ recycle.removeEntry(e); } } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java index d46b92b0..38ebea7f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java @@ -23,7 +23,7 @@ * * @author Jo */ -public abstract class AbstractEntry, E extends Entry> implements Entry { +public abstract class AbstractEntry implements Entry { @Override public boolean matchTitle(String text){ @@ -57,7 +57,7 @@ public boolean match(Entry.Matcher matcher) { @Override public String getPath() { - G parent = this.getParent(); + Group parent = this.getParent(); String result = ""; if (parent != null) { result = parent.getPath(); diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java index 5f9c045c..2caff000 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java @@ -29,12 +29,12 @@ * * @author Jo */ -public abstract class AbstractGroup, E extends Entry> implements Group { +public abstract class AbstractGroup implements Group { @Override - public List findGroups(String group) { - ArrayList result = new ArrayList<>(); - for (G g: getGroups()) { + public List findGroups(String group) { + ArrayList result = new ArrayList<>(); + for (Group g: getGroups()) { if (g.getName().equals(group)) { result.add(g); } @@ -43,20 +43,20 @@ public List findGroups(String group) { } @Override - public List findEntries(String find, boolean recursive) { + public List findEntries(String find, boolean recursive) { return findEntries(entry -> entry.match(find), recursive); } @Override - public List findEntries(Entry.Matcher matcher, boolean recursive) { - List result = new ArrayList<>(getEntries().size()); - for (E entry: getEntries()){ + public List findEntries(Entry.Matcher matcher, boolean recursive) { + List result = new ArrayList<>(getEntries().size()); + for (Entry entry: getEntries()){ if (entry.match(matcher)){ result.add(entry); } } if (recursive) { - for (G group: getGroups()) { + for (Group group: getGroups()) { // don't recurse into recycle bin if (group.isRecycleBin()) { continue; @@ -68,21 +68,21 @@ public List findEntries(Entry.Matcher matcher, boolean recursive) { } @Override - public void copy(Group parent) { - for (Group child : parent.getGroups()) { - G addedGroup = addGroup(this.getDatabase().newGroup(child)); + public void copy(Group parent) { + for (Group child : parent.getGroups()) { + Group addedGroup = addGroup(this.getDatabase().newGroup(child)); addedGroup.copy(child); } - for (Entry entry : parent.getEntries()) { + for (Entry entry : parent.getEntries()) { addEntry(this.getDatabase().newEntry(entry)); } } @Override public String getPath() { - Stack parents = new Stack<>(); - G parent = (G)this; - parents.push((G)this); + Stack parents = new Stack<>(); + Group parent = this; + parents.push(this); while ((parent=parent.getParent()) != null) { parents.push(parent); } diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index 964e360c..3c0a915e 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -13,7 +13,7 @@ * establishes {@link org.linguafranca.pwdb.PropertyValue.StringStore} storage for unprotected values and * {@link org.linguafranca.pwdb.PropertyValue.SealedStore} for protected values. */ -public abstract class ProtectedDatabase, E extends Entry> extends AbstractDatabase { +public abstract class ProtectedDatabase extends AbstractDatabase { private PropertyValue.Strategy valueStrategy = new PropertyValue.Strategy.Default(); @Override diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index aef1180b..67d484e0 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -37,13 +37,13 @@ * @author jo */ @SuppressWarnings("WeakerAccess") -public abstract class QuickStart, G extends Group, E extends Entry> { +public abstract class QuickStart { static PrintStream printStream = getTestPrintStream(); - public abstract D getDatabase(); - public abstract D loadDatabase(Credentials creds, InputStream inputStream); + public abstract Database getDatabase(); + public abstract Database loadDatabase(Credentials creds, InputStream inputStream); @BeforeClass public static void ensureOutputDir() throws IOException { @@ -59,7 +59,7 @@ public void loadKdbx() throws IOException { // password credentials Credentials credentials = new KdbxCreds("123".getBytes()); // Jaxb implementation seems a lot faster than the DOM implementation - D database = loadDatabase(credentials, inputStream); + Database database = loadDatabase(credentials, inputStream); // visit all groups and entries and list them to console database.visit(new Visitor.Print(printStream)); } @@ -69,17 +69,17 @@ public void loadKdbx() throws IOException { * Save KDBX */ - private E entryFactory(D database, String s, int e) { + private Entry entryFactory(Database database, String s, int e) { return database.newEntry(String.format("Group %s Entry %d", s, e)); } public void saveKdbx() throws IOException { // create an empty database - D database = getDatabase(); + Database database = getDatabase(); // add some groups and entries for (int g = 0; g < 5; g++) { - G group = database.getRootGroup().addGroup(database.newGroup(Integer.toString(g))); + Group group = database.getRootGroup().addGroup(database.newGroup(Integer.toString(g))); for (int e = 0; e <= g; e++) { // entry factory is a local helper to populate an entry group.addEntry(entryFactory(database, Integer.toString(g), e)); @@ -95,22 +95,22 @@ public void saveKdbx() throws IOException { /** * Splice - add a group from one database to a parent from another (or copy from the same db) */ - public void splice(G newParent, Group groupToSplice) { - G addedGroup = newParent.addGroup(newParent.getDatabase().newGroup(groupToSplice)); + public void splice(Group newParent, Group groupToSplice) { + Group addedGroup = newParent.addGroup(newParent.getDatabase().newGroup(groupToSplice)); addedGroup.copy(groupToSplice); } /** * Group by title */ - public G groupByTitle(D database) { - List entries = database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); + public Group groupByTitle(Database database) { + List entries = database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); // create a new group using DB factory method - G newParent = database.newGroup("Found entries"); + Group newParent = database.newGroup("Found entries"); // iterate over the found entries - for (E entry : entries) { + for (Entry entry : entries) { // copy the entry using DB factory so that it remains where it was found as well as being in new group - E copy = database.newEntry(entry); + Entry copy = database.newEntry(entry); // add new entry to new group newParent.addEntry(copy); } @@ -129,7 +129,7 @@ public void loadKdb() throws IOException { // visit all groups and entries and list them to console // create a KDBX (database - D kdbxDatabase = getDatabase(); + Database kdbxDatabase = getDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDB Database to KDBX Database"); // deep copy from group (not including source group, KDB database has simulated root) @@ -153,7 +153,7 @@ public void loadKdbx3SaveKdbx4(String resourceName, byte[] password, OutputStrea } // create a KDBX (database - D kdbxDatabase = getDatabase(); + Database kdbxDatabase = getDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDBX 3 Database to KDBX 4 Database"); // deep copy from group (not including source group, KDB database has simulated root) @@ -184,14 +184,14 @@ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, OutputStrea database = JacksonDatabase.load(credentials, inputStream); } - // create a KDBX (database - D kdbxDatabase = getDatabase(); + // create a KDBX (database) + Database kdbxDatabase = getDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDBX 4 Database to KDBX 3 Database"); - // deep copy from group (not including source group, KDB database has simulated root) + // deep copy from group (not including source group kdbxDatabase.getRootGroup().copy(database.getRootGroup()); - // choose a stream format - V4 Kdbx and choose some algorithms + // choose a stream format - V3 KdbxHeader kdbxHeader = new KdbxHeader(KdbxHeader.KdbxHeaderOpts.V3_AES_SALSA_20); KdbxStreamFormat formatV3 = new KdbxStreamFormat(kdbxHeader); diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java index bec1d689..2c8557d7 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java @@ -18,7 +18,7 @@ public class Util { @FunctionalInterface public interface DatabaseLoader { - Database load(Credentials c, InputStream i) throws IOException; + Database load(Credentials c, InputStream i) throws IOException; } List dbLoader = Arrays.asList(JacksonDatabase::load); @@ -48,7 +48,7 @@ public static void listDatabase(String resourceName, Credentials creds, OutputSt * Example shows how to list XML from a database using specified loader */ public static void listDatabase(DatabaseLoader loader, String resourceName, Credentials creds, OutputStream outputStream) throws IOException { - Database database = loader.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); + Database database = loader.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); database.save(new StreamFormat.None(), new KdbxCreds.None(), outputStream); } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index b85b145e..76a9e9ac 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -36,7 +36,7 @@ * * @author jo */ -public class SimpleQuickStartTest extends QuickStart { +public class SimpleQuickStartTest extends QuickStart { @Override diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java index b0f46328..bd055a0e 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java @@ -3,6 +3,7 @@ import com.google.common.io.CharStreams; import org.junit.Ignore; import org.junit.Test; +import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.kdbx.Helpers; import org.linguafranca.pwdb.kdbx.KdbxCreds; import org.linguafranca.pwdb.kdbx.KdbxHeader; @@ -30,9 +31,9 @@ public void testIssue27() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); KdbxCreds creds = new KdbxCreds("passwordless".getBytes()); JacksonDatabase db = JacksonDatabase.load(creds, is); - List entries = db.findEntries("testtitle"); + List entries = db.findEntries("testtitle"); - for (JacksonEntry entry: entries) { + for (Entry entry: entries) { printStream.println(Helpers.fromDateV3(entry.getCreationTime())); assertEquals("2021-01-11T09:18:56Z", Helpers.fromDateV3(entry.getCreationTime())); } @@ -46,9 +47,9 @@ public void testV4Date() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("V4-AES-AES.kdbx"); KdbxCreds creds = new KdbxCreds("123".getBytes()); JacksonDatabase db = JacksonDatabase.load(creds, is); - List entries = db.findEntries("Sample Entry #2 - Copy"); + List entries = db.findEntries("Sample Entry #2 - Copy"); - for (JacksonEntry entry: entries) { + for (Entry entry: entries) { printStream.println(Helpers.fromDate(entry.getCreationTime())); assertEquals("2018-01-26T13:20:58Z", Helpers.fromDateV3(entry.getCreationTime())); } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java index 7207922c..ae82b0ae 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java @@ -1,6 +1,7 @@ package org.linguafranca.pwdb.kdbx.validation; import org.junit.Test; +import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.kdbx.KdbxCreds; import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; @@ -26,8 +27,8 @@ public void testV2Keyfile() throws IOException { KdbxCreds creds = new KdbxCreds("MyPassword".getBytes(), keyStream); assert databaseStream != null; JacksonDatabase database = JacksonDatabase.load(creds, databaseStream); - List entries = database.findEntries("Sample Entry"); - JacksonEntry entry = entries.get(0); + List entries = database.findEntries("Sample Entry"); + Entry entry = entries.get(0); printStream.println(entry.getTitle()); } } diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java index 9236bdec..9d139b75 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java @@ -34,7 +34,7 @@ import static org.linguafranca.pwdb.kdbx.jackson.JacksonSerializableDatabase.createEmptyDatabase; -public class JacksonDatabase extends ProtectedDatabase { +public class JacksonDatabase extends ProtectedDatabase { KeePassFile keePassFile; StreamFormat streamFormat; @@ -154,9 +154,9 @@ public void enableRecycleBin(boolean enable) { @Override public JacksonGroup getRecycleBin() { UUID recycleBinUuid = this.keePassFile.meta.recycleBinUUID; - JacksonGroup g = findGroup(recycleBinUuid); + JacksonGroup g = (JacksonGroup) findGroup(recycleBinUuid); if (g == null && isRecycleBinEnabled()) { - g = newGroup("Recycle Bin"); + g = (JacksonGroup) newGroup("Recycle Bin"); getRootGroup().addGroup(g); this.keePassFile.meta.recycleBinUUID = g.getUuid(); this.keePassFile.meta.recycleBinChanged = new Date(); diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java index fca607b2..3c0ec5a4 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java @@ -65,7 +65,32 @@ @JsonIgnoreProperties({"path", "username", "title", "notes", "url", "password"}) -public class JacksonEntry extends AbstractEntry { +public class JacksonEntry extends AbstractEntry { + + @JsonIgnore + JacksonDatabase database; + + @JsonIgnore + JacksonGroup parent; + + protected JacksonEntry() { + string = new ArrayList<>(); + binary = new ArrayList<>(); + times = new Times(); + uuid = UUID.randomUUID(); + iconID = 0; + } + + public static JacksonEntry createEntry(JacksonDatabase database) { + JacksonEntry result = new JacksonEntry(); + result.database = database; + result.parent = null; + // avoiding setProperty as it does a touch(); + for (String p : STANDARD_PROPERTY_NAMES) { + result.string.add(new StringProperty(p, database.getPropertyValueStrategy().newUnprotected().of(""))); + } + return result; + } @JacksonXmlProperty(localName = "UUID") @@ -110,31 +135,6 @@ public class JacksonEntry extends AbstractEntry { @JacksonXmlProperty(localName = "History") /* Workaround jackson */ protected JacksonHistory history; - @JsonIgnore - JacksonDatabase database; - - @JsonIgnore - JacksonGroup parent; - - protected JacksonEntry() { - string = new ArrayList<>(); - binary = new ArrayList<>(); - times = new Times(); - uuid = UUID.randomUUID(); - iconID = 0; - } - - public static JacksonEntry createEntry(JacksonDatabase database) { - JacksonEntry result = new JacksonEntry(); - result.database = database; - result.parent = null; - // avoiding setProperty as it does a touch(); - for (String p : STANDARD_PROPERTY_NAMES) { - result.string.add(new StringProperty(p, database.getPropertyValueStrategy().newUnprotected().of(""))); - } - return result; - } - @Override @JsonIgnore public String getProperty(String s) { diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java index 8ec0edb9..25eecdea 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java +++ b/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java @@ -24,6 +24,8 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; import org.jetbrains.annotations.NotNull; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.base.AbstractGroup; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; @@ -53,7 +55,7 @@ "group", }) @JsonIgnoreProperties(ignoreUnknown=true) -public class JacksonGroup extends AbstractGroup{ +public class JacksonGroup extends AbstractGroup{ @JacksonXmlProperty(localName = "UUID") @JsonDeserialize(converter = Base64ToUUIDConverter.class) @@ -146,7 +148,8 @@ public JacksonGroup getParent() { } @Override - public void setParent(JacksonGroup group) { + public void setParent(Group g) { + JacksonGroup group = (JacksonGroup) g; if (isRootGroup()) { throw new IllegalStateException("Cannot add root group to another group"); } @@ -174,7 +177,8 @@ public int getGroupsCount() { } @Override - public JacksonGroup addGroup(JacksonGroup group) { + public JacksonGroup addGroup(Group g) { + JacksonGroup group = (JacksonGroup) g; if (group.isRootGroup()) { throw new IllegalStateException("Cannot add root group to another group"); } @@ -191,7 +195,8 @@ public JacksonGroup addGroup(JacksonGroup group) { } @Override - public JacksonGroup removeGroup(JacksonGroup group) { + public JacksonGroup removeGroup(Group g) { + JacksonGroup group = (JacksonGroup) g; if (this.database != group.database) { throw new IllegalStateException("Must be from same database"); } @@ -213,7 +218,8 @@ public int getEntriesCount() { } @Override - public JacksonEntry addEntry(JacksonEntry entry) { + public JacksonEntry addEntry(Entry e) { + JacksonEntry entry = (JacksonEntry) e; if (this.database != entry.database) { throw new IllegalStateException("Must be from same database"); } @@ -227,7 +233,8 @@ public JacksonEntry addEntry(JacksonEntry entry) { } @Override - public JacksonEntry removeEntry(JacksonEntry entry) { + public JacksonEntry removeEntry(Entry e) { + JacksonEntry entry = (JacksonEntry) e; if (this.database != entry.database) { throw new IllegalStateException("Must be from same database"); } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java b/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java index ac60d0e0..5b0053da 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java +++ b/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java @@ -28,14 +28,14 @@ import java.io.InputStream; import java.io.OutputStream; -public class JacksonPropertyValueTest extends PropertyValueChecks { +public class JacksonPropertyValueTest extends PropertyValueChecks { public JacksonPropertyValueTest() throws IOException { super(true); } @Override - public void saveDatabase(JacksonDatabase database, Credentials credentials, OutputStream outputStream) throws IOException { + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { database.save(credentials, outputStream); } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java b/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java index 06c74ab8..eb79badd 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java +++ b/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java @@ -39,7 +39,7 @@ import org.linguafranca.pwdb.kdbx.jackson.JacksonIcon; -public class JacksonSaveAndReloadTest extends SaveAndReloadChecks{ +public class JacksonSaveAndReloadTest extends SaveAndReloadChecks{ @Override public JacksonDatabase getDatabase() { @@ -58,7 +58,7 @@ public JacksonDatabase getDatabase(String name, Credentials credentials) throws } @Override - public void saveDatabase(JacksonDatabase database, Credentials credentials, OutputStream outputStream) throws IOException { + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { database.save(credentials, outputStream); } diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java index 93d062b8..88a2b56c 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java @@ -30,7 +30,7 @@ * * @author jo */ -public class KdbDatabase extends AbstractDatabase { +public class KdbDatabase extends AbstractDatabase { private String description; private final KdbGroup rootGroup; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index 180c2e06..d1747fd6 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -32,7 +32,7 @@ * * @author jo */ -public class KdbEntry extends AbstractEntry { +public class KdbEntry extends AbstractEntry { KdbGroup parent; private UUID uuid = UUID.randomUUID(); private String title = ""; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index b027ca10..f6877da2 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -20,6 +20,7 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; +import org.linguafranca.pwdb.base.AbstractEntry; import org.linguafranca.pwdb.base.AbstractGroup; import org.linguafranca.pwdb.Entry; @@ -32,7 +33,7 @@ * The class holds a KDB Group * @author jo */ -public class KdbGroup extends AbstractGroup { +public class KdbGroup extends AbstractGroup { private boolean root; private KdbGroup parent; protected KdbDatabase database; @@ -55,7 +56,8 @@ public class KdbGroup extends AbstractGroup { } @Override - public KdbGroup addGroup(KdbGroup group) { + public KdbGroup addGroup(Group g) { + KdbGroup group = (KdbGroup) g; groups.add(group); if (group.getParent() != null) { group.getParent().removeGroup(group); @@ -65,7 +67,8 @@ public KdbGroup addGroup(KdbGroup group) { } @Override - public KdbGroup removeGroup(KdbGroup group) { + public KdbGroup removeGroup(Group g) { + KdbGroup group = (KdbGroup) g; groups.remove(group); group.parent = null; return group; @@ -82,7 +85,8 @@ public int getEntriesCount() { } @Override - public KdbEntry addEntry(KdbEntry entry) { + public KdbEntry addEntry(Entry e) { + KdbEntry entry = ((KdbEntry) e); KdbGroup entryParent = entry.parent; if (entryParent != null) { entryParent.removeEntry(entry); @@ -93,7 +97,8 @@ public KdbEntry addEntry(KdbEntry entry) { } @Override - public KdbEntry removeEntry(KdbEntry entry) { + public KdbEntry removeEntry(Entry e) { + KdbEntry entry = ((KdbEntry) e); entries.remove(entry); entry.parent = null; return entry; @@ -144,7 +149,7 @@ public KdbGroup getParent() { } @Override - public void setParent(KdbGroup parent) { + public void setParent(Group parent) { parent.addGroup(this); } diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java index 772e68d4..6e2de18a 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java @@ -34,11 +34,11 @@ /** * @author Jo */ -public abstract class BasicDatabaseChecks , E extends Entry> { +public abstract class BasicDatabaseChecks { - protected Database database; + protected Database database; - public abstract Database createDatabase() throws IOException; + public abstract Database createDatabase() throws IOException; public BasicDatabaseChecks() throws IOException { this.database = createDatabase(); @@ -53,14 +53,14 @@ public void testEmptyDatabase() { @Test public void testAddGroup() { - G g1 = database.getRootGroup().addGroup(database.newGroup("group1")); + Group g1 = database.getRootGroup().addGroup(database.newGroup("group1")); assertEquals(1, database.getRootGroup().getGroups().size()); assertEquals("group1", g1.getName()); assertEquals(0, g1.getGroups().size()); assertEquals(0, g1.getEntries().size()); assertEquals("root is not the parent of its child", g1.getParent(), database.getRootGroup()); - G g2 = database.newGroup(); + Group g2 = database.newGroup(); assertEquals("", g2.getName()); Assert.assertNotNull(g2.getUuid()); assertEquals(0, g2.getIcon().getIndex()); @@ -75,12 +75,12 @@ public void testAddGroup() { @Test public void testDeleteGroup () { - Group g1 = database.getRootGroup().addGroup(database.newGroup("group1")); - List l1 = database.getRootGroup().findGroups("group1"); + Group g1 = database.getRootGroup().addGroup(database.newGroup("group1")); + List l1 = database.getRootGroup().findGroups("group1"); assertEquals(1, l1.size()); - G g2 = l1.get(0); + Group g2 = l1.get(0); assertEquals(g2, g1); - G g3 = database.getRootGroup().removeGroup(g2); + Group g3 = database.getRootGroup().removeGroup(g2); assertEquals(g3, g1); assertNull(g1.getParent()); assertEquals(0, database.getRootGroup().getGroups().size()); @@ -89,13 +89,13 @@ public void testDeleteGroup () { @Test public void testAddRemoveEntry() { - E e1 = database.getRootGroup().addEntry(database.newEntry()); + Entry e1 = database.getRootGroup().addEntry(database.newEntry()); e1.setTitle("entry1"); - List l1 = database.findEntries("entry1"); + List l1 = database.findEntries("entry1"); assertEquals(1, l1.size()); - E e12 = database.getRootGroup().addEntry(database.newEntry("entry12")); - List l2 = database.findEntries("entry1"); + Entry e12 = database.getRootGroup().addEntry(database.newEntry("entry12")); + List l2 = database.findEntries("entry1"); assertEquals(2, l2.size()); // show that the entries are different @@ -106,7 +106,7 @@ public void testAddRemoveEntry() { assertEquals(2, database.findEntries("entry1").size()); // show that we get an equivalent entry when we remove to when we inserted - E e12b = database.getRootGroup().removeEntry(e12); + Entry e12b = database.getRootGroup().removeEntry(e12); assertEquals(e12b, e12); // has been unhooked from parent assertNull(e12.getParent()); @@ -115,7 +115,7 @@ public void testAddRemoveEntry() { @Test public void testSetFields () { - E e1 = database.newEntry("Entry 1"); + Entry e1 = database.newEntry("Entry 1"); e1.setNotes("this looks a little like Entry 2"); assertEquals("this looks a little like Entry 2", e1.getNotes()); e1.setUsername("jake@window.com"); @@ -170,7 +170,7 @@ public void testSetFields () { @Test public void testTimes() { long beforeSecond = Instant.now().toEpochMilli()/1000; - E entry = database.newEntry(); + Entry entry = database.newEntry(); long afterSecond = Instant.now().toEpochMilli()/1000; long createdSecond = entry.getCreationTime().getTime()/1000; @@ -191,7 +191,7 @@ public void testTimes() { @Test public void checkAddChangeRemoveProperty() { // only applies to databases that support arbitrary properties - E entry = database.newEntry(); + Entry entry = database.newEntry(); assertEquals(Entry.STANDARD_PROPERTY_NAMES.size(), entry.getPropertyNames().size()); try { entry.setProperty("test", "test1"); @@ -219,7 +219,7 @@ public void checkAddChangeRemoveProperty() { @Test public void testNewEntry() { - E e2 = database.newEntry(); + Entry e2 = database.newEntry(); Assert.assertNull(e2.getParent()); assertEquals("", e2.getPassword()); Assert.assertNotNull(e2.getUuid()); @@ -235,7 +235,7 @@ public void testNewEntry() { @Test public void testCopy() throws IOException { - E entry1 = database.newEntry(); + Entry entry1 = database.newEntry(); entry1.setTitle("Entry"); entry1.setUsername("Username"); entry1.setPassword("Password"); @@ -244,9 +244,9 @@ public void testCopy() throws IOException { entry1.setIcon(database.newIcon(2)); // create a new Database - Database database2 = createDatabase(); + Database database2 = createDatabase(); // create a new Entry in new Database - E entry2 = database2.newEntry(entry1); + Entry entry2 = database2.newEntry(entry1); assertEquals(entry1.getTitle(), entry2.getTitle()); assertEquals(entry1.getUsername(), entry2.getUsername()); @@ -256,11 +256,11 @@ public void testCopy() throws IOException { assertEquals(entry1.getIcon(), entry2.getIcon()); assertNotEquals(entry1.getUuid(), entry2.getUuid()); - G group1 = database.newGroup(); + Group group1 = database.newGroup(); group1.setName("Group"); group1.setIcon(database.newIcon(3)); - G group2 = database2.newGroup(group1); + Group group2 = database2.newGroup(group1); assertEquals(group1.getName(), group2.getName()); assertEquals(group1.getIcon(), group2.getIcon()); assertNotEquals(group1.getUuid(), group2.getUuid()); diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java index 206d9309..886dded5 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java @@ -48,14 +48,14 @@ * * @author jo */ -public abstract class BinaryPropertyChecks , G extends Group, E extends Entry> { +public abstract class BinaryPropertyChecks { - public D database; + public Database database; @SuppressWarnings("unused") - public abstract void saveDatabase(D database, Credentials credentials, OutputStream outputStream) throws IOException; - public abstract D loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; - public abstract D newDatabase(); + public abstract void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; + public abstract Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; + public abstract Database newDatabase(); public abstract Credentials getCreds(byte[] creds); /** @@ -63,7 +63,7 @@ public abstract class BinaryPropertyChecks , G extends */ @Test public void getBinaryProperty() throws Exception { - E entry = database.findEntries("Test attachment").get(0); + Entry entry = database.findEntries("Test attachment").get(0); byte [] letterJ = entry.getBinaryProperty("letter J.jpeg"); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); assert testFile != null; @@ -76,7 +76,7 @@ public void getBinaryProperty() throws Exception { */ @Test public void getAnotherBinaryProperty() throws Exception { - E entry = database.findEntries("Test 2 attachment").get(0); + Entry entry = database.findEntries("Test 2 attachment").get(0); byte [] letterL = entry.getBinaryProperty("letter L.jpeg"); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); assert testFile != null; @@ -92,7 +92,7 @@ public void setBinaryProperty() throws Exception { InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); assert testFile != null; byte [] original = ByteStreams.toByteArray(testFile); - E entry = database.findEntries("Test attachment").get(0); + Entry entry = database.findEntries("Test attachment").get(0); entry.setBinaryProperty("letter L.jpeg", original); byte [] letterL = entry.getBinaryProperty("letter L.jpeg"); Assert.assertArrayEquals(original, letterL); @@ -105,7 +105,7 @@ public void setBinaryProperty() throws Exception { */ @Test public void getBinaryPropertyNames() { - E entry = database.findEntries("Test attachment").get(0); + Entry entry = database.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg"}, entry.getBinaryPropertyNames().toArray()); entry = database.findEntries("Test 2 attachment").get(0); @@ -128,7 +128,7 @@ public void checkSupported(){ public void checkAddChangeRemoveBinaryProperty() { byte[] test = new byte[] {0, 1, 2 ,3}; byte[] test2 = new byte[] {3, 2, 1, 0}; - E entry = database.findEntries("Test attachment").get(0); + Entry entry = database.findEntries("Test attachment").get(0); assertEquals(1, entry.getBinaryPropertyNames().size()); entry.setBinaryProperty("test", test); assertArrayEquals(test, entry.getBinaryProperty("test")); @@ -152,10 +152,10 @@ public void saveAndReloadCheck() throws IOException { Path file = Files.createTempFile("keepass", "tmp"); saveDatabase(database, getCreds("123".getBytes()), Files.newOutputStream(file)); - D db = loadDatabase(getCreds("123".getBytes()),Files.newInputStream(file)); - E newEntry = db.findEntries("Test attachment").get(0); + Database db = loadDatabase(getCreds("123".getBytes()),Files.newInputStream(file)); + Entry newEntry = db.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); - E oldEntry = database.findEntries("Test attachment").get(0); + Entry oldEntry = database.findEntries("Test attachment").get(0); assertArrayEquals(oldEntry.getBinaryProperty("letter J.jpeg"), newEntry.getBinaryProperty("letter J.jpeg")); newEntry = db.findEntries("Test 2 attachment").get(0); @@ -172,8 +172,8 @@ public void saveAndReloadCheck() throws IOException { @Test public void createAndSaveCheck() throws IOException { Path file = Files.createTempFile("keepass", "tmp"); - D database1 = newDatabase(); - E entry = database1.newEntry("Test attachment"); + Database database1 = newDatabase(); + Entry entry = database1.newEntry("Test attachment"); database1.getRootGroup().addEntry(entry); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); assert testFile != null; @@ -181,8 +181,8 @@ public void createAndSaveCheck() throws IOException { entry.setBinaryProperty("letter J.jpeg", letterJ); saveDatabase(database1, getCreds("123".getBytes()), Files.newOutputStream(file)); - D db = loadDatabase(getCreds("123".getBytes()),Files.newInputStream(file)); - E entry1 = db.findEntries("Test attachment").get(0); + Database db = loadDatabase(getCreds("123".getBytes()),Files.newInputStream(file)); + Entry entry1 = db.findEntries("Test attachment").get(0); // just one property assertArrayEquals(new String[] {"letter J.jpeg"}, entry1.getBinaryPropertyNames().toArray()); // content is correct diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java index 3f7b7642..f63f4448 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java @@ -32,11 +32,11 @@ /** * @author jo */ -public abstract class DatabaseLoaderChecks , G extends Group, E extends Entry> { +public abstract class DatabaseLoaderChecks { static PrintStream printStream = getTestPrintStream(); - protected Database database; + protected Database database; protected boolean skipDateCheck = false; private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssX"); /** @@ -50,11 +50,11 @@ public void test123File() throws ParseException { // find all entries in the database // the kdb version has three additional system related entries - List anything = database.findEntries(""); + List anything = database.findEntries(""); Assert.assertTrue(10 <= anything.size()); // find all entries in the database that have the string "test" in them - List tests = database.findEntries("test"); + List tests = database.findEntries("test"); for (Entry tes: tests) { printStream.println(tes.getTitle()); } @@ -73,14 +73,14 @@ public void test123File() throws ParseException { Assert.assertEquals("123", pass2); } - List passwords = database.findEntries("password"); + List passwords = database.findEntries("password"); Assert.assertEquals(4, passwords.size()); for (Entry passwordEntry : passwords) { assertEquals(passwordEntry.getTitle(), passwordEntry.getPassword()); printStream.println(passwordEntry.getTitle()); } - List entries = database.findEntries(new Entry.Matcher() { + List entries = database.findEntries(new Entry.Matcher() { @Override public boolean matches(Entry entry) { return entry.getTitle().equals("hello world"); diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java index 503244d4..d82f5329 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java @@ -32,14 +32,14 @@ /** * Testing the operation of PropertyValue mechanisms */ -public abstract class PropertyValueChecks, G extends Group, E extends Entry> { +public abstract class PropertyValueChecks { - public abstract void saveDatabase(D database, Credentials credentials, OutputStream outputStream) throws IOException; - public abstract D loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; + public abstract void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; + public abstract Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; public abstract Credentials getCreds(byte[] creds); private final boolean propertyValueSupported; - protected D database; + protected Database database; @BeforeClass public static void ensureOutputDir() throws IOException { @@ -51,7 +51,7 @@ public PropertyValueChecks(boolean propertyValueSupported) throws IOException { this.propertyValueSupported = propertyValueSupported; } - public abstract D createDatabase() throws IOException; + public abstract Database createDatabase() throws IOException; /** * Catches expected thrown exception @@ -84,7 +84,7 @@ public void noPropertyValueSupport() { catchException((z) -> database.setShouldProtect(null, true), UnsupportedOperationException.class); catchException((z) -> database.listShouldProtect(), UnsupportedOperationException.class); - E entry = database.newEntry(); + Entry entry = database.newEntry(); catchException((z) -> entry.getPropertyValue(null), UnsupportedOperationException.class); catchException((z) -> entry.setPropertyValue(null, (PropertyValue) null), UnsupportedOperationException.class); @@ -149,7 +149,7 @@ public PropertyValue.Factory newUnprotected() { assertTrue(pv.isProtected()); // create an entry with random property as protected value - E entry = database.newEntry("Test Random"); + Entry entry = database.newEntry("Test Random"); entry.setPropertyValue("random", pv); database.getRootGroup().addEntry(entry); @@ -162,9 +162,9 @@ public PropertyValue.Factory newUnprotected() { // reload database, "random" is still protected even though it's not protected by default FileInputStream fis = new FileInputStream("testOutput/test9.kdbx"); - D input = loadDatabase(getCreds("123".getBytes()), fis); + Database input = loadDatabase(getCreds("123".getBytes()), fis); - List entries = input.findEntries("random"); + List entries = input.findEntries("random"); assertEquals(1, entries.size()); assertTrue(entries.get(0).getPropertyValue("random").isProtected()); assertFalse(input.getPropertyValueStrategy().getProtectedProperties().contains("random")); diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java index 10caec9a..21f06bce 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java @@ -11,9 +11,9 @@ /** * @author jo */ -public abstract class RecycleBinChecks , G extends Group, E extends Entry> { +public abstract class RecycleBinChecks { - protected Database database; + protected Database database; @Test public void recycleBinEntries() { @@ -22,10 +22,10 @@ public void recycleBinEntries() { assertNull(database.getRecycleBin()); database.enableRecycleBin(true); assertTrue(database.isRecycleBinEnabled()); - G recycleBin = database.getRecycleBin(); + Group recycleBin = database.getRecycleBin(); assertNotNull(recycleBin); - E entry = database.newEntry(); + Entry entry = database.newEntry(); assertEquals(entry, database.getRootGroup().addEntry(entry)); assertEquals(1, database.getRootGroup().getEntriesCount()); @@ -51,10 +51,10 @@ public void recycleBinEntries() { public void recycleBinGroups() { database.enableRecycleBin(true); assertTrue(database.isRecycleBinEnabled()); - G recycleBin = database.getRecycleBin(); + Group recycleBin = database.getRecycleBin(); assertNotNull(recycleBin); - G group= database.newGroup(); + Group group= database.newGroup(); assertEquals(group, database.getRootGroup().addGroup(group)); assertEquals(2, database.getRootGroup().getGroupsCount()); diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java b/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java index cb40dd2d..5a428ff3 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java +++ b/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java @@ -33,15 +33,15 @@ /** * @author jo */ -public abstract class SaveAndReloadChecks , G extends Group, E extends Entry> { +public abstract class SaveAndReloadChecks { static PrintStream printStream = getTestPrintStream(); - public abstract D getDatabase(); - public abstract D getDatabase(String name, Credentials credentials) throws IOException; + public abstract Database getDatabase(); + public abstract Database getDatabase(String name, Credentials credentials) throws IOException; - public abstract void saveDatabase(D database, Credentials credentials, OutputStream outputStream) throws IOException; - public abstract D loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; + public abstract void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; + public abstract Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; public abstract Credentials getCreds(byte[] creds); public abstract boolean verifyStreamFormat (StreamFormat s1, StreamFormat s2); @@ -60,7 +60,7 @@ public void saveAndReloadTest() throws IOException { long now = System.currentTimeMillis(); // create database with known content - D output = createNewDatabase(); + Database output = createNewDatabase(); verifyContents(output); //output.save(new StreamFormat.None(), new Credentials.None(), printStream); @@ -75,7 +75,7 @@ public void saveAndReloadTest() throws IOException { FileInputStream fis = new FileInputStream("testOutput/test1.kdbx"); - D input = loadDatabase(getCreds("123".getBytes()), fis); + Database input = loadDatabase(getCreds("123".getBytes()), fis); verifyContents(input); //input.save(new StreamFormat.None(), new Credentials.None(), printStream); printStream.format("Test took %d millis", System.currentTimeMillis() - now); @@ -86,12 +86,12 @@ public void saveAndReloadTest() throws IOException { */ @Test public void saveAndReloadTest2() throws IOException { - D attachment = getDatabase("Attachment.kdbx", getCreds("123".getBytes())); + Database attachment = getDatabase("Attachment.kdbx", getCreds("123".getBytes())); - E entry = attachment.findEntries("Test attachment").get(0); + Entry entry = attachment.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg"}, entry.getBinaryPropertyNames().toArray()); - E entry2 = attachment.findEntries("Test 2 attachment").get(0); + Entry entry2 = attachment.findEntries("Test 2 attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry2.getBinaryPropertyNames().toArray()); byte[] content = entry2.getBinaryProperty("letter L.jpeg"); @@ -104,7 +104,7 @@ public void saveAndReloadTest2() throws IOException { fos.close(); FileInputStream fis = new FileInputStream("testOutput/test2.kdbx"); - D input = loadDatabase(getCreds("123".getBytes()), fis); + Database input = loadDatabase(getCreds("123".getBytes()), fis); entry = input.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); @@ -123,23 +123,23 @@ public void saveAndReloadTest2() throws IOException { @Test public void saveAndReloadTest3() throws IOException { for (String resource: testFiles) { - D database = getDatabase(resource, this.getCreds("123".getBytes())); + Database database = getDatabase(resource, this.getCreds("123".getBytes())); StreamFormat format1 = database.getStreamFormat(); database.save(getCreds("123".getBytes()), Files.newOutputStream(Paths.get("testOutput/test3.kdbx"))); FileInputStream fis = new FileInputStream("testOutput/test3.kdbx"); - D input = loadDatabase(getCreds("123".getBytes()), fis); + Database input = loadDatabase(getCreds("123".getBytes()), fis); StreamFormat format2 = input.getStreamFormat(); assertTrue(verifyStreamFormat(format1, format2)); } } - private D createNewDatabase() throws IOException { - D database = getDatabase(); + private Database createNewDatabase() throws IOException { + Database database = getDatabase(); for (@SuppressWarnings("WrapperTypeMayBePrimitive") Integer g = 0; g < 5; g++){ - G group = database.getRootGroup().addGroup(database.newGroup(g.toString())); + Group group = database.getRootGroup().addGroup(database.newGroup(g.toString())); for (int e = 0; e <= g; e++) { group.addEntry(entryFactory(database, g.toString(), e)); } @@ -148,8 +148,8 @@ private D createNewDatabase() throws IOException { return database; } - private E entryFactory(D database, String g, int e) { - E result = database.newEntry(); + private Entry entryFactory(Database database, String g, int e) { + Entry result = database.newEntry(); result.setTitle(g + "-" + e); result.setUsername(g + " - un - " + e); result.setPassword(g + "- p -" + e); @@ -158,15 +158,15 @@ private E entryFactory(D database, String g, int e) { return result; } - private void verifyContents(D database) { + private void verifyContents(Database database) { for (Integer g = 0; g< 5; g++){ - G group = database.getRootGroup().getGroups().get(g); + Group group = database.getRootGroup().getGroups().get(g); assertEquals(g.toString(), group.getName()); assertEquals(g + 1, group.getEntries().size()); assertEquals(g+1, group.getEntriesCount()); assertEquals(database.getRootGroup(), group.getParent()); for (int e = 0; e <= g; e++) { - E entry = group.getEntries().get(e); + Entry entry = group.getEntries().get(e); assertEquals(g + "-" + e, entry.getTitle()); assertEquals(g + " - un - " + e, entry.getUsername()); assertEquals(g + "- p -" + e, entry.getPassword()); @@ -184,7 +184,7 @@ private void verifyContents(D database) { */ @Test @Ignore public void saveNewDatabase () throws IOException { - D database = createNewDatabase(); + Database database = createNewDatabase(); FileOutputStream outputStream = new FileOutputStream("compatibility.kdbx"); saveDatabase(database, getCreds("123".getBytes()), outputStream); @@ -196,7 +196,7 @@ public void saveNewDatabase () throws IOException { */ @Test @Ignore public void inspectNewDatabase () throws IOException { - D database = createNewDatabase(); + Database database = createNewDatabase(); database.visit(new Visitor.Print(printStream)); } @@ -205,8 +205,8 @@ public void inspectNewDatabase () throws IOException { // the assertions here somewhat duplicate those in BasicDatabaseChecks @Test public void testNewDatabase() throws IOException { - D database = getDatabase(); - G root = database.getRootGroup(); + Database database = getDatabase(); + Group root = database.getRootGroup(); Assert.assertTrue(root.isRootGroup()); assertEquals(0, root.getGroups().size()); assertEquals(0, root.getEntries().size()); @@ -223,7 +223,7 @@ public void testNewDatabase() throws IOException { database.setDescription("Test Database"); assertEquals("Test Database", database.getDescription()); - G group1 = database.newGroup("Group 1"); + Group group1 = database.newGroup("Group 1"); UUID newGroupUUID = group1.getUuid(); root.addGroup(group1); @@ -250,7 +250,7 @@ public void testNewDatabase() throws IOException { } catch (Exception ignored) { } - G group2 = database.newGroup(); + Group group2 = database.newGroup(); group2.setName("Group 2"); group1.addGroup(group2); assertEquals(1, group1.getGroups().size()); @@ -260,7 +260,7 @@ public void testNewDatabase() throws IOException { assertEquals(0, group1.getGroups().size()); assertEquals(2, root.getGroups().size()); - E entry1 = database.newEntry(); + Entry entry1 = database.newEntry(); entry1.setTitle("A new entry"); assertEquals("A new entry", entry1.getTitle()); entry1.setUsername("user name"); @@ -277,7 +277,7 @@ public void testNewDatabase() throws IOException { entry1.setPassword("pass"); assertEquals("pass", entry1.getPassword()); - E entry2 = database.newEntry(entry1); + Entry entry2 = database.newEntry(entry1); entry2.setPassword("pass2"); assertEquals("pass2", entry2.getPassword()); group2.addEntry(entry2); From e426c18d3cb9f63598d29d17f78677dab68fcd4e Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 13 Sep 2024 17:29:54 +0100 Subject: [PATCH 05/66] Update module and package structure, rename Jackson* files to Kdbx* --- CHANGELOG.md | 15 ++- ModuleStructure.svg | 2 +- all/pom.xml | 2 +- .../linguafranca/pwdb/kdbx/ChooseFile.java | 3 +- .../linguafranca/pwdb/kdbx/OpenDbExample.java | 4 +- .../linguafranca/pwdb/kdbx/QuickStart.java | 12 ++- .../org/linguafranca/pwdb/kdbx/SaxParse.java | 3 + .../java/org/linguafranca/pwdb/kdbx/Util.java | 10 +- .../linguafranca/pwdb/kdbx/SaxParseTest.java | 1 + .../pwdb/kdbx/SimpleQuickStartTest.java | 17 ++-- .../org/linguafranca/pwdb/kdbx/UtilTest.java | 1 + .../pwdb/kdbx/validation/Issue27Test.java | 15 ++- .../pwdb/kdbx/validation/Issue33Test.java | 8 +- .../pwdb/kdbx/validation/Issue38Test.java | 7 +- {jackson => kdbx-database}/pom.xml | 4 +- .../linguafranca/pwdb/kdbx/KdbxDatabase.java | 60 ++++++------ .../org/linguafranca/pwdb/kdbx/KdbxEntry.java | 24 ++--- .../org/linguafranca/pwdb/kdbx/KdbxGroup.java | 48 +++++----- .../linguafranca/pwdb/kdbx/KdbxHistory.java | 12 +-- .../org/linguafranca/pwdb/kdbx/KdbxIcon.java | 10 +- .../pwdb/kdbx/KdbxSerializableDatabase.java | 20 ++-- .../converter/Base64ToByteConverter.java | 0 .../converter/Base64ToUUIDConverter.java | 2 +- .../converter/BooleanToStringConverter.java | 2 +- .../converter/ByteToBase64Converter.java | 0 .../converter/DateToStringConverter.java | 2 +- .../converter/StringToBooleanConverter.java | 2 +- .../converter/StringToDateConverter.java | 2 +- .../converter/UUIDToBase64Converter.java | 2 +- .../jackson/converter/ValueDeserializer.java | 5 +- .../jackson/converter/ValueSerializer.java | 0 .../pwdb/kdbx/jackson/model/EntryClasses.java | 6 +- .../pwdb/kdbx/jackson/model/KeePassFile.java | 4 +- .../pwdb/kdbx/jackson/model/Times.java | 0 .../pwdb/JacksonBinaryPropertyV3Test.java | 10 +- .../pwdb/JacksonBinaryPropertyV4Test.java | 10 +- .../pwdb/JacksonPropertyValueTest.java | 17 ++-- .../pwdb/JacksonRecycleBinTest.java | 4 +- .../pwdb/JacksonSaveAndReloadTest.java | 29 ++---- .../pwdb/KdbxDatabaseLoadTest.java | 16 ++-- .../pwdb/KdbxDatabaseLoaderTest.java | 10 +- .../linguafranca/pwdb/KdbxDatabaseTest.java | 8 +- {kdbx => kdbx-io}/pom.xml | 2 +- .../linguafranca/pwdb/format}/Helpers.java | 2 +- .../pwdb/format}/KdbxCredentials.java | 2 +- .../linguafranca/pwdb/format}/KdbxCreds.java | 2 +- .../linguafranca/pwdb/format}/KdbxHeader.java | 4 +- .../pwdb/format}/KdbxKeyFile.java | 2 +- .../pwdb/format}/KdbxSerializer.java | 2 +- .../pwdb/format}/KdbxStreamFormat.java | 2 +- .../hashedblock/CollectingInputStream.java | 0 .../hashedblock/CollectingOutputStream.java | 0 .../hashedblock/HashedBlockInputStream.java | 0 .../hashedblock/HashedBlockOutputStream.java | 0 .../hashedblock/HmacBlockInputStream.java | 9 +- .../hashedblock/HmacBlockOutputStream.java | 9 +- .../src/main/resources/base.kdbx.xml | 0 .../pwdb/hashedblock/HashBlockTest.java | 0 .../linguafranca/pwdb/kdbx/HelperTest.java | 1 + .../linguafranca/pwdb/kdbx/HelpersTest.java | 4 +- .../pwdb/kdbx/KdbxHeaderTest.java | 6 +- .../pwdb/kdbx/KdbxKeyFileTest.java | 4 + .../pwdb/kdbx/KdbxSerializerTest.java | 3 + .../pwdb/security/StreamEncryptionTest.java | 0 pom.xml | 4 +- readme.md | 93 +++++-------------- 66 files changed, 261 insertions(+), 299 deletions(-) rename {jackson => kdbx-database}/pom.xml (94%) rename jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java => kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxDatabase.java (71%) rename jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java => kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxEntry.java (95%) rename jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java => kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxGroup.java (87%) rename jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonHistory.java => kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHistory.java (82%) rename jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonIcon.java => kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxIcon.java (86%) rename jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java => kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializableDatabase.java (91%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java (100%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java (95%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java (95%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java (100%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java (95%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java (95%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java (96%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java (95%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java (93%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java (100%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java (96%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java (99%) rename {jackson => kdbx-database}/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java (100%) rename {jackson => kdbx-database}/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java (85%) rename {jackson => kdbx-database}/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java (85%) rename {jackson => kdbx-database}/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java (66%) rename {jackson => kdbx-database}/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java (89%) rename {jackson => kdbx-database}/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java (66%) rename jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoadTest.java => kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java (78%) rename jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoaderTest.java => kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java (75%) rename jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseTest.java => kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java (79%) rename {kdbx => kdbx-io}/pom.xml (97%) rename {kdbx/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-io/src/main/java/org/linguafranca/pwdb/format}/Helpers.java (99%) rename {kdbx/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-io/src/main/java/org/linguafranca/pwdb/format}/KdbxCredentials.java (98%) rename {kdbx/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-io/src/main/java/org/linguafranca/pwdb/format}/KdbxCreds.java (98%) rename {kdbx/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-io/src/main/java/org/linguafranca/pwdb/format}/KdbxHeader.java (99%) rename {kdbx/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-io/src/main/java/org/linguafranca/pwdb/format}/KdbxKeyFile.java (99%) rename {kdbx/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-io/src/main/java/org/linguafranca/pwdb/format}/KdbxSerializer.java (99%) rename {kdbx/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-io/src/main/java/org/linguafranca/pwdb/format}/KdbxStreamFormat.java (99%) rename {kdbx => kdbx-io}/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java (100%) rename {kdbx => kdbx-io}/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java (100%) rename {kdbx => kdbx-io}/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java (100%) rename {kdbx => kdbx-io}/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java (100%) rename {kdbx => kdbx-io}/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java (94%) rename {kdbx => kdbx-io}/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java (94%) rename {kdbx => kdbx-io}/src/main/resources/base.kdbx.xml (100%) rename {kdbx => kdbx-io}/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java (100%) rename {kdbx => kdbx-io}/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java (97%) rename {kdbx => kdbx-io}/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java (94%) rename {kdbx => kdbx-io}/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java (95%) rename {kdbx => kdbx-io}/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java (97%) rename {kdbx => kdbx-io}/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java (96%) rename {kdbx => kdbx-io}/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index c7a0aead..ca68e6df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,20 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.com) and [Semantic Versioning](http://semver.org/spec/v2.0.0.html) -## [2.2.3-SNAPSHOT] +## 2024-09-13 [2.3-SNAPSHOT] + +### Changed + +- remove Simple database +- remove JAXB database +- remove DOM database +- remove complicated generics on database classes + +### Added + +- added a service provider interface for extensibility + +## 2024-09-10 [2.2.3-SNAPSHOT] ### Added diff --git a/ModuleStructure.svg b/ModuleStructure.svg index 9579dffe..58d79fd0 100644 --- a/ModuleStructure.svg +++ b/ModuleStructure.svg @@ -1,4 +1,4 @@ -
all
all
simple
simple
jaxb
jaxb
dom
dom
kdb
kdb
kdbx
kdbx
database
database
Test
Test
example
example
Other, future
Other,...
KDBX Implementations
KDBX I...
KDB Implementation
KDB Im...
???
???
Implementatiions
of the 
org.linguafranca.pwdb.Database Interface
Implem...
jackson
jackson
Text is not SVG - cannot display
\ No newline at end of file +
all
all
kdb
kdb
kdbx-io
kdbx-io
database
database
Test
Test
example
example
Other, future
Other,...
KDBX Implementations
KDBX I...
KDB Implementation
KDB Im...
???
???
Implementatiions
of the 
org.linguafranca.pwdb.Database Interface
Implem...
kdbx-database
kdbx-database
???
???
Text is not SVG - cannot display
\ No newline at end of file diff --git a/all/pom.xml b/all/pom.xml index d075ffb2..e59dcefc 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -37,7 +37,7 @@
org.linguafranca.pwdb - KeePassJava2-jackson + KeePassJava2-kdbx-database ${project.version}
diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java index e0f4f266..761a52a3 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java @@ -1,7 +1,7 @@ package org.linguafranca.pwdb.kdbx; import com.google.common.base.Strings; -import org.linguafranca.util.HexViewer; +import org.linguafranca.pwdb.format.KdbxCreds; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,7 +9,6 @@ import javax.swing.filechooser.FileFilter; import java.io.*; import java.net.URL; -import java.nio.file.Files; import java.nio.file.Paths; import static org.linguafranca.util.TestUtil.getTestPrintStream; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index 9b794453..f95d2074 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -2,7 +2,7 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.format.KdbxCreds; import java.io.InputStream; import java.io.PrintStream; @@ -25,7 +25,7 @@ private interface DbLoader { private static class JacksonDbLoader implements DbLoader { @Override public Database load(KdbxCreds creds, InputStream inputStream) throws Exception { - return JacksonDatabase.load(creds, inputStream); + return KdbxDatabase.load(creds, inputStream); } } diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 67d484e0..0ab4a0c6 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -18,9 +18,11 @@ import org.junit.BeforeClass; import org.linguafranca.pwdb.*; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdb.KdbCredentials; import org.linguafranca.pwdb.kdb.KdbDatabase; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; import org.linguafranca.pwdb.security.Encryption; import java.io.*; @@ -144,12 +146,12 @@ public void loadKdb() throws IOException { * Load KDBX V3 save as KDBX V4 */ public void loadKdbx3SaveKdbx4(String resourceName, byte[] password, OutputStream v4OutputStream) throws IOException { - JacksonDatabase database; + KdbxDatabase database; // password credentials KdbxCreds credentials = new KdbxCreds(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = JacksonDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, inputStream); } // create a KDBX (database @@ -176,12 +178,12 @@ public void loadKdbx3SaveKdbx4(String resourceName, byte[] password, OutputStrea * Load KDBX V3 save as KDBX V4 */ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, OutputStream v3OutputStream) throws IOException { - JacksonDatabase database; + KdbxDatabase database; // password credentials KdbxCreds credentials = new KdbxCreds(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = JacksonDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, inputStream); } // create a KDBX (database) diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java index 2128b9d2..0c15cb58 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java @@ -17,6 +17,9 @@ package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.format.Helpers; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; import org.linguafranca.pwdb.security.StreamEncryptor; import org.xml.sax.*; import org.xml.sax.helpers.DefaultHandler; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java index 2c8557d7..62f34d1a 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java @@ -4,7 +4,9 @@ import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; import java.io.*; import java.nio.charset.StandardCharsets; @@ -14,14 +16,14 @@ public class Util { - List implementations = new ArrayList<>(Arrays.asList(JacksonDatabase.class)); + List implementations = new ArrayList<>(Arrays.asList(KdbxDatabase.class)); @FunctionalInterface public interface DatabaseLoader { Database load(Credentials c, InputStream i) throws IOException; } - List dbLoader = Arrays.asList(JacksonDatabase::load); + List dbLoader = Arrays.asList(KdbxDatabase::load); public static InputStream getDecryptedInputStream (String resourceName, Credentials credentials) throws IOException { return getDecryptedInputStream(resourceName, credentials, new KdbxHeader()); @@ -40,7 +42,7 @@ public static String streamToString(InputStream inputStream) throws IOException * Example shows how to list XML from a database (but not decrypted passwords) */ public static void listDatabase(String resourceName, Credentials creds, OutputStream outputStream) throws IOException { - JacksonDatabase database = JacksonDatabase.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); + KdbxDatabase database = KdbxDatabase.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); database.save(new StreamFormat.None(), new KdbxCreds.None(), outputStream); } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java index 1d673046..b0b7318b 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java @@ -17,6 +17,7 @@ package org.linguafranca.pwdb.kdbx; import org.junit.Test; +import org.linguafranca.pwdb.format.KdbxCreds; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index 76a9e9ac..a223c71c 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -18,9 +18,8 @@ import org.junit.Test; import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; -import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; -import org.linguafranca.pwdb.kdbx.jackson.JacksonGroup; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.security.Encryption; import java.io.IOException; @@ -40,14 +39,14 @@ public class SimpleQuickStartTest extends QuickStart { @Override - public JacksonDatabase getDatabase() { - return new JacksonDatabase(); + public KdbxDatabase getDatabase() { + return new KdbxDatabase(); } @Override - public JacksonDatabase loadDatabase(Credentials credentials, InputStream inputStream){ + public KdbxDatabase loadDatabase(Credentials credentials, InputStream inputStream){ try { - return JacksonDatabase.load(credentials, inputStream); + return KdbxDatabase.load(credentials, inputStream); } catch (Exception e) { throw new IllegalStateException(e); } @@ -75,7 +74,7 @@ public void loadSave() throws IOException { loadKdbx3SaveKdbx4("test123.kdbx","123".getBytes(), Files.newOutputStream(path)); // load newly created V4 database - JacksonDatabase db = JacksonDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); + KdbxDatabase db = KdbxDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(4, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.CHA_CHA_20, streamFormat.getStreamConfiguration().getCipherAlgorithm()); @@ -89,7 +88,7 @@ public void loadSave2() throws IOException { loadKdbx4SaveKdbx3("V4-ChaCha20-Argon2-Attachment.kdbx","123".getBytes(), Files.newOutputStream(path)); // load newly created V4 database - JacksonDatabase db = JacksonDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); + KdbxDatabase db = KdbxDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(3, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.AES, streamFormat.getStreamConfiguration().getCipherAlgorithm()); diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java index bfda2c5b..f7a74d03 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java @@ -1,6 +1,7 @@ package org.linguafranca.pwdb.kdbx; import org.junit.Test; +import org.linguafranca.pwdb.format.KdbxCreds; import java.io.IOException; import java.io.OutputStream; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java index bd055a0e..a385dc88 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java @@ -4,12 +4,11 @@ import org.junit.Ignore; import org.junit.Test; import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxSerializer; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; -import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; +import org.linguafranca.pwdb.format.Helpers; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; import java.io.IOException; import java.io.InputStream; @@ -30,7 +29,7 @@ public class Issue27Test { public void testIssue27() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); KdbxCreds creds = new KdbxCreds("passwordless".getBytes()); - JacksonDatabase db = JacksonDatabase.load(creds, is); + KdbxDatabase db = KdbxDatabase.load(creds, is); List entries = db.findEntries("testtitle"); for (Entry entry: entries) { @@ -46,7 +45,7 @@ public void testIssue27() throws IOException { public void testV4Date() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("V4-AES-AES.kdbx"); KdbxCreds creds = new KdbxCreds("123".getBytes()); - JacksonDatabase db = JacksonDatabase.load(creds, is); + KdbxDatabase db = KdbxDatabase.load(creds, is); List entries = db.findEntries("Sample Entry #2 - Copy"); for (Entry entry: entries) { diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java index 4a05bed7..b732aef4 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java @@ -6,10 +6,10 @@ import org.junit.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.KdbxCreds; +import org.linguafranca.pwdb.format.Helpers; +import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.kdbx.Util; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; import java.io.*; import java.nio.file.Files; @@ -42,7 +42,7 @@ public void refreshInputStream() { @Test public void testJacksonDatabase() throws IOException { - JacksonDatabase database = JacksonDatabase.load(CREDENTIALS, inputStream); + KdbxDatabase database = KdbxDatabase.load(CREDENTIALS, inputStream); database.save(new StreamFormat.None(), new Credentials.None(), Files.newOutputStream(Paths.get(TEST_OUTPUT_DIR, "Issue33Jackson.xml"))); } } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java index ae82b0ae..777f1607 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java @@ -2,9 +2,8 @@ import org.junit.Test; import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; -import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; import java.io.IOException; import java.io.InputStream; @@ -26,7 +25,7 @@ public void testV2Keyfile() throws IOException { assert keyStream != null; KdbxCreds creds = new KdbxCreds("MyPassword".getBytes(), keyStream); assert databaseStream != null; - JacksonDatabase database = JacksonDatabase.load(creds, databaseStream); + KdbxDatabase database = KdbxDatabase.load(creds, databaseStream); List entries = database.findEntries("Sample Entry"); Entry entry = entries.get(0); printStream.println(entry.getTitle()); diff --git a/jackson/pom.xml b/kdbx-database/pom.xml similarity index 94% rename from jackson/pom.xml rename to kdbx-database/pom.xml index d6e5b535..faa622df 100644 --- a/jackson/pom.xml +++ b/kdbx-database/pom.xml @@ -23,14 +23,14 @@ 4.0.0 - KeePassJava2-jackson + KeePassJava2-kdbx-database KeePassJava2 :: Jackson Contains a Jackson XML implementation for KDBX. org.linguafranca.pwdb - KeePassJava2-kdbx + KeePassJava2-kdbx-io ${project.version} diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxDatabase.java similarity index 71% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxDatabase.java index 9d139b75..476ce8a1 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxDatabase.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx.jackson; +package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.StreamConfiguration; import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxStreamFormat; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdbx.jackson.model.KeePassFile; import org.linguafranca.pwdb.protect.ProtectedDatabase; @@ -32,23 +32,23 @@ import java.util.Objects; import java.util.UUID; -import static org.linguafranca.pwdb.kdbx.jackson.JacksonSerializableDatabase.createEmptyDatabase; +import static org.linguafranca.pwdb.kdbx.KdbxSerializableDatabase.createEmptyDatabase; -public class JacksonDatabase extends ProtectedDatabase { +public class KdbxDatabase extends ProtectedDatabase { KeePassFile keePassFile; StreamFormat streamFormat; - public JacksonDatabase() { + public KdbxDatabase() { this(createEmptyDatabase(), null); } - public JacksonDatabase(KeePassFile file, StreamFormat streamFormat) { + public KdbxDatabase(KeePassFile file, StreamFormat streamFormat) { try { keePassFile = file; keePassFile.root.group.database = this; this.streamFormat = streamFormat; - JacksonSerializableDatabase.fixUp(keePassFile.root.group); + KdbxSerializableDatabase.fixUp(keePassFile.root.group); } catch (Exception e) { throw new IllegalStateException(e); } @@ -61,11 +61,11 @@ public JacksonDatabase(KeePassFile file, StreamFormat streamFormat) { * @return a new Database * @throws Exception on load failure */ - public static JacksonDatabase loadXml(InputStream inputStream) throws Exception { + public static KdbxDatabase loadXml(InputStream inputStream) throws Exception { - KeePassFile keePassFile = new JacksonSerializableDatabase().load(inputStream).keePassFile; + KeePassFile keePassFile = new KdbxSerializableDatabase().load(inputStream).keePassFile; keePassFile.root.group.uuid = UUID.randomUUID(); - return new JacksonDatabase(keePassFile, null); + return new KdbxDatabase(keePassFile, null); } /** @@ -75,11 +75,11 @@ public static JacksonDatabase loadXml(InputStream inputStream) throws Exception * @param inputStream where to load from * @return a new database */ - public static JacksonDatabase load(Credentials credentials, InputStream inputStream) throws IOException { - JacksonSerializableDatabase jsd = new JacksonSerializableDatabase(); + public static KdbxDatabase load(Credentials credentials, InputStream inputStream) throws IOException { + KdbxSerializableDatabase jsd = new KdbxSerializableDatabase(); StreamFormat streamFormat = new KdbxStreamFormat(); streamFormat.load(jsd, credentials, inputStream); - return new JacksonDatabase(jsd.keePassFile, streamFormat); + return new KdbxDatabase(jsd.keePassFile, streamFormat); } /** @@ -108,35 +108,35 @@ public void save(Credentials credentials, OutputStream outputStream) throws IOEx public void save(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) throws IOException { keePassFile.meta.generator = "KeePassJava2-Jackson"; - JacksonSerializableDatabase jacksonSerializableDatabase = new JacksonSerializableDatabase(this.keePassFile); - jacksonSerializableDatabase.setPropertyValueStrategy(this.getPropertyValueStrategy()); - streamFormat.save(jacksonSerializableDatabase, credentials, outputStream); + KdbxSerializableDatabase kdbxSerializableDatabase = new KdbxSerializableDatabase(this.keePassFile); + kdbxSerializableDatabase.setPropertyValueStrategy(this.getPropertyValueStrategy()); + streamFormat.save(kdbxSerializableDatabase, credentials, outputStream); setDirty(false); } @Override - public JacksonGroup getRootGroup() { + public KdbxGroup getRootGroup() { return keePassFile.root.group; } @Override - public JacksonGroup newGroup() { - return JacksonGroup.createGroup(this); + public KdbxGroup newGroup() { + return KdbxGroup.createGroup(this); } @Override - public JacksonEntry newEntry() { - return JacksonEntry.createEntry(this); + public KdbxEntry newEntry() { + return KdbxEntry.createEntry(this); } @Override - public JacksonIcon newIcon() { - return new JacksonIcon(); + public KdbxIcon newIcon() { + return new KdbxIcon(); } @Override - public JacksonIcon newIcon(Integer integer) { - JacksonIcon ic = newIcon(); + public KdbxIcon newIcon(Integer integer) { + KdbxIcon ic = newIcon(); ic.setIndex(integer); return ic; } @@ -152,11 +152,11 @@ public void enableRecycleBin(boolean enable) { } @Override - public JacksonGroup getRecycleBin() { + public KdbxGroup getRecycleBin() { UUID recycleBinUuid = this.keePassFile.meta.recycleBinUUID; - JacksonGroup g = (JacksonGroup) findGroup(recycleBinUuid); + KdbxGroup g = (KdbxGroup) findGroup(recycleBinUuid); if (g == null && isRecycleBinEnabled()) { - g = (JacksonGroup) newGroup("Recycle Bin"); + g = (KdbxGroup) newGroup("Recycle Bin"); getRootGroup().addGroup(g); this.keePassFile.meta.recycleBinUUID = g.getUuid(); this.keePassFile.meta.recycleBinChanged = new Date(); @@ -198,7 +198,7 @@ public List getBinaries() { } public void addBinary(byte[] bytes, int index) { - JacksonSerializableDatabase.addBinary(this.keePassFile, index, bytes); + KdbxSerializableDatabase.addBinary(this.keePassFile, index, bytes); } public StreamFormat getStreamFormat() { diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxEntry.java similarity index 95% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxEntry.java index 3c0ec5a4..d870d049 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxEntry.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx.jackson; +package org.linguafranca.pwdb.kdbx; import java.util.ArrayList; import java.util.Date; @@ -26,7 +26,7 @@ import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.base.AbstractEntry; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.UUIDToBase64Converter; @@ -65,15 +65,15 @@ @JsonIgnoreProperties({"path", "username", "title", "notes", "url", "password"}) -public class JacksonEntry extends AbstractEntry { +public class KdbxEntry extends AbstractEntry { @JsonIgnore - JacksonDatabase database; + KdbxDatabase database; @JsonIgnore - JacksonGroup parent; + KdbxGroup parent; - protected JacksonEntry() { + protected KdbxEntry() { string = new ArrayList<>(); binary = new ArrayList<>(); times = new Times(); @@ -81,8 +81,8 @@ protected JacksonEntry() { iconID = 0; } - public static JacksonEntry createEntry(JacksonDatabase database) { - JacksonEntry result = new JacksonEntry(); + public static KdbxEntry createEntry(KdbxDatabase database) { + KdbxEntry result = new KdbxEntry(); result.database = database; result.parent = null; // avoiding setProperty as it does a touch(); @@ -133,7 +133,7 @@ public static JacksonEntry createEntry(JacksonDatabase database) { protected AutoType autoType; @JacksonXmlProperty(localName = "History") /* Workaround jackson */ - protected JacksonHistory history; + protected KdbxHistory history; @Override @JsonIgnore @@ -273,7 +273,7 @@ public List getBinaryPropertyNames() { } @Override - public JacksonGroup getParent() { + public KdbxGroup getParent() { return parent; } @@ -283,8 +283,8 @@ public JacksonGroup getParent() { } @Override - public JacksonIcon getIcon() { - return new JacksonIcon(iconID); + public KdbxIcon getIcon() { + return new KdbxIcon(iconID); } @Override diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxGroup.java similarity index 87% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxGroup.java index 25eecdea..5069826d 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxGroup.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx.jackson; +package org.linguafranca.pwdb.kdbx; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -55,7 +55,7 @@ "group", }) @JsonIgnoreProperties(ignoreUnknown=true) -public class JacksonGroup extends AbstractGroup{ +public class KdbxGroup extends AbstractGroup{ @JacksonXmlProperty(localName = "UUID") @JsonDeserialize(converter = Base64ToUUIDConverter.class) @@ -104,27 +104,27 @@ public class JacksonGroup extends AbstractGroup{ @JacksonXmlProperty(localName = "Entry") /* Workaround jackson */ @JacksonXmlElementWrapper(useWrapping = false) - protected List entries; + protected List entries; @JacksonXmlProperty(localName = "Group") /* Workaround jackson */ @JacksonXmlElementWrapper(useWrapping = false) - protected List groups; + protected List groups; @JsonIgnore - protected JacksonDatabase database; + protected KdbxDatabase database; @JsonIgnore - protected JacksonGroup parent; + protected KdbxGroup parent; - public JacksonGroup() { + public KdbxGroup() { entries = new ArrayList<>(); groups = new ArrayList<>(); times = new Times(); } - public static JacksonGroup createGroup(JacksonDatabase database) { - JacksonGroup group = new JacksonGroup(); + public static KdbxGroup createGroup(KdbxDatabase database) { + KdbxGroup group = new KdbxGroup(); group.database = database; group.iconID = 0; group.name = ""; @@ -143,13 +143,13 @@ public boolean isRecycleBin() { } @Override - public JacksonGroup getParent() { + public KdbxGroup getParent() { return parent; } @Override public void setParent(Group g) { - JacksonGroup group = (JacksonGroup) g; + KdbxGroup group = (KdbxGroup) g; if (isRootGroup()) { throw new IllegalStateException("Cannot add root group to another group"); } @@ -166,7 +166,7 @@ public void setParent(Group g) { } @Override - public List getGroups() { + public List getGroups() { return new ArrayList<>(groups); } @@ -177,8 +177,8 @@ public int getGroupsCount() { } @Override - public JacksonGroup addGroup(Group g) { - JacksonGroup group = (JacksonGroup) g; + public KdbxGroup addGroup(Group g) { + KdbxGroup group = (KdbxGroup) g; if (group.isRootGroup()) { throw new IllegalStateException("Cannot add root group to another group"); } @@ -195,8 +195,8 @@ public JacksonGroup addGroup(Group g) { } @Override - public JacksonGroup removeGroup(Group g) { - JacksonGroup group = (JacksonGroup) g; + public KdbxGroup removeGroup(Group g) { + KdbxGroup group = (KdbxGroup) g; if (this.database != group.database) { throw new IllegalStateException("Must be from same database"); } @@ -207,7 +207,7 @@ public JacksonGroup removeGroup(Group g) { } @Override - public List getEntries() { + public List getEntries() { return new ArrayList<>(this.entries); } @@ -218,8 +218,8 @@ public int getEntriesCount() { } @Override - public JacksonEntry addEntry(Entry e) { - JacksonEntry entry = (JacksonEntry) e; + public KdbxEntry addEntry(Entry e) { + KdbxEntry entry = (KdbxEntry) e; if (this.database != entry.database) { throw new IllegalStateException("Must be from same database"); } @@ -233,8 +233,8 @@ public JacksonEntry addEntry(Entry e) { } @Override - public JacksonEntry removeEntry(Entry e) { - JacksonEntry entry = (JacksonEntry) e; + public KdbxEntry removeEntry(Entry e) { + KdbxEntry entry = (KdbxEntry) e; if (this.database != entry.database) { throw new IllegalStateException("Must be from same database"); } @@ -260,8 +260,8 @@ public UUID getUuid() { } @Override - public JacksonIcon getIcon() { - return new JacksonIcon(iconID); + public KdbxIcon getIcon() { + return new KdbxIcon(iconID); } @Override @@ -272,7 +272,7 @@ public void setIcon(Icon icon) { @NotNull @Override - public JacksonDatabase getDatabase() { + public KdbxDatabase getDatabase() { return database; } diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonHistory.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHistory.java similarity index 82% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonHistory.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHistory.java index c9ea238c..a35d78e4 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonHistory.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHistory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.linguafranca.pwdb.kdbx.jackson; +package org.linguafranca.pwdb.kdbx; import java.util.ArrayList; import java.util.List; @@ -21,27 +21,27 @@ import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -public class JacksonHistory { +public class KdbxHistory { @JacksonXmlProperty(localName = "Entry") /* Workaround jackson */ @JacksonXmlElementWrapper(useWrapping = false) - private List entry; + private List entry; - public JacksonHistory() { + public KdbxHistory() { entry = new ArrayList<>(); } /** * @return the entry */ - public List getEntry() { + public List getEntry() { return entry; } /** * @param entry the entry to set */ - public void setEntry(List entry) { + public void setEntry(List entry) { this.entry = entry; } diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonIcon.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxIcon.java similarity index 86% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonIcon.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxIcon.java index 7e3cb732..cf196f1c 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonIcon.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxIcon.java @@ -14,22 +14,22 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx.jackson; +package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.Icon; /** * @author giusvale */ -public class JacksonIcon implements Icon { +public class KdbxIcon implements Icon { private int index; - public JacksonIcon(int iconID) { + public KdbxIcon(int iconID) { index = iconID; } - public JacksonIcon() { + public KdbxIcon() { this.index = 0; } @@ -50,7 +50,7 @@ public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; - JacksonIcon that = (JacksonIcon) o; + KdbxIcon that = (KdbxIcon) o; return index == that.index; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializableDatabase.java similarity index 91% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializableDatabase.java index 694b001b..b2eb6376 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/JacksonSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializableDatabase.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.linguafranca.pwdb.kdbx.jackson; +package org.linguafranca.pwdb.kdbx; import com.ctc.wstx.api.WstxInputProperties; import com.ctc.wstx.api.WstxOutputProperties; @@ -24,7 +24,7 @@ import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.SerializableDatabase; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.kdbx.jackson.converter.ValueDeserializer; import org.linguafranca.pwdb.kdbx.jackson.converter.ValueSerializer; import org.linguafranca.pwdb.kdbx.jackson.model.KeePassFile; @@ -38,7 +38,7 @@ import java.io.OutputStreamWriter; import java.util.Objects; -public class JacksonSerializableDatabase implements SerializableDatabase { +public class KdbxSerializableDatabase implements SerializableDatabase { public KeePassFile keePassFile; private StreamEncryptor encryptor; @@ -47,7 +47,7 @@ public class JacksonSerializableDatabase implements SerializableDatabase { public static KeePassFile createEmptyDatabase() { - InputStream inputStream = JacksonSerializableDatabase.class.getClassLoader() + InputStream inputStream = KdbxSerializableDatabase.class.getClassLoader() .getResourceAsStream("base.kdbx.xml"); XmlMapper mapper = new XmlMapper(); try { @@ -58,15 +58,15 @@ public static KeePassFile createEmptyDatabase() { } - public JacksonSerializableDatabase() { + public KdbxSerializableDatabase() { } - public JacksonSerializableDatabase(KeePassFile keePassFile) { + public KdbxSerializableDatabase(KeePassFile keePassFile) { this.keePassFile = keePassFile; } @Override - public JacksonSerializableDatabase load(InputStream inputStream) throws IOException { + public KdbxSerializableDatabase load(InputStream inputStream) throws IOException { XmlMapper mapper = new XmlMapper(); SimpleModule module = new SimpleModule(); module.addDeserializer(PropertyValue.class, new ValueDeserializer(encryptor, propertyValueStrategy)); @@ -161,15 +161,15 @@ public int getBinaryCount() { * * @param parent a parent to recurse */ - static void fixUp(JacksonGroup parent) { + static void fixUp(KdbxGroup parent) { - for (JacksonGroup group : parent.groups) { + for (KdbxGroup group : parent.groups) { group.parent = parent; group.database = parent.database; fixUp(group); } - for (JacksonEntry entry : parent.entries) { + for (KdbxEntry entry : parent.entries) { entry.database = parent.database; entry.parent = parent; } diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java similarity index 100% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java similarity index 95% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java index b2b07d29..1b80f953 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java @@ -18,7 +18,7 @@ import java.util.UUID; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import com.fasterxml.jackson.databind.util.StdConverter; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java similarity index 95% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java index 4059b1c8..ca1c4384 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java @@ -16,7 +16,7 @@ package org.linguafranca.pwdb.kdbx.jackson.converter; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import com.fasterxml.jackson.databind.util.StdConverter; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java similarity index 100% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java similarity index 95% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java index 752579c0..2fb214b9 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java @@ -18,7 +18,7 @@ import java.util.Date; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import com.fasterxml.jackson.databind.util.StdConverter; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java similarity index 95% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java index b295edd9..7b13542c 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java @@ -15,7 +15,7 @@ */ package org.linguafranca.pwdb.kdbx.jackson.converter; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import com.fasterxml.jackson.databind.util.StdConverter; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java similarity index 96% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java index c215f90e..07f80808 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java @@ -18,7 +18,7 @@ import java.util.Date; import java.util.Objects; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import com.fasterxml.jackson.databind.util.StdConverter; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java similarity index 95% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java index 3564185e..e843a0b7 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java @@ -17,7 +17,7 @@ import java.util.UUID; -import org.linguafranca.pwdb.kdbx.Helpers; +import org.linguafranca.pwdb.format.Helpers; import com.fasterxml.jackson.databind.util.StdConverter; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java similarity index 93% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java index 3c9639b4..08294a07 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java @@ -20,14 +20,11 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; import org.apache.commons.codec.binary.Base64; -import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.PropertyValue; -import org.linguafranca.pwdb.kdbx.Helpers; -import org.linguafranca.pwdb.kdbx.jackson.model.EntryClasses; +import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.security.StreamEncryptor; import java.io.IOException; -import java.nio.charset.StandardCharsets; public class ValueDeserializer extends StdDeserializer { diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java similarity index 100% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java similarity index 96% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java index c768d4eb..580afc70 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java @@ -20,7 +20,7 @@ import java.util.List; import org.linguafranca.pwdb.PropertyValue; -import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; +import org.linguafranca.pwdb.kdbx.KdbxEntry; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.StringToBooleanConverter; @@ -145,13 +145,13 @@ public static class History { @JacksonXmlProperty(localName = "Entry") /* Workaround jackson */ @JacksonXmlElementWrapper(useWrapping = false) - private List list; + private List list; public History() { list = new ArrayList<>(); } - public List getHistory() { + public List getHistory() { return list; } } diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java similarity index 99% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java index 6c7691bd..6ce1b750 100644 --- a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java @@ -22,7 +22,7 @@ import java.util.UUID; import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.kdbx.jackson.JacksonGroup; +import org.linguafranca.pwdb.kdbx.KdbxGroup; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToByteConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; @@ -62,7 +62,7 @@ public Root() { } @JacksonXmlProperty(localName = "Group") - public JacksonGroup group; + public KdbxGroup group; @JacksonXmlProperty(localName = "DeletedObjects") protected Object deletedObject; diff --git a/jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java similarity index 100% rename from jackson/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java similarity index 85% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java index 41c14567..3ad55d1d 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java @@ -21,8 +21,8 @@ import java.io.OutputStream; import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; public class JacksonBinaryPropertyV3Test extends BinaryPropertyChecks { @@ -30,7 +30,7 @@ public class JacksonBinaryPropertyV3Test extends BinaryPropertyChecks { public JacksonBinaryPropertyV3Test() { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); try { - database = JacksonDatabase.load(new KdbxCreds("123".getBytes()),inputStream); + database = KdbxDatabase.load(new KdbxCreds("123".getBytes()),inputStream); } catch (Exception e) { throw new IllegalStateException(e); } @@ -43,14 +43,14 @@ public void saveDatabase(Database database, Credentials credentials, OutputStrea @Override public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return JacksonDatabase.load(credentials, inputStream); + return KdbxDatabase.load(credentials, inputStream); } @Override public Database newDatabase() { Database result = null; try { - result = new JacksonDatabase(); + result = new KdbxDatabase(); } catch(Exception e) { throw new RuntimeException(e); } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java similarity index 85% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java index ec65bdc4..f738dd41 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java @@ -20,15 +20,15 @@ import java.io.OutputStream; import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; public class JacksonBinaryPropertyV4Test extends BinaryPropertyChecks{ public JacksonBinaryPropertyV4Test() throws Exception{ InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-Argon2-Attachment.kdbx"); - database = JacksonDatabase.load(new KdbxCreds("123".getBytes()), inputStream); + database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); } @@ -40,7 +40,7 @@ public void saveDatabase(Database database, Credentials credentials, OutputStrea @Override public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { try { - return JacksonDatabase.load(credentials, inputStream); + return KdbxDatabase.load(credentials, inputStream); } catch (Exception e) { throw new RuntimeException(e); } @@ -50,7 +50,7 @@ public Database loadDatabase(Credentials credentials, InputStream inputStream) t public Database newDatabase() { Database result = null; try { - result = new JacksonDatabase(); + result = new KdbxDatabase(); } catch (Exception e) { throw new RuntimeException(e); } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java similarity index 66% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java index 5b0053da..0cdb915a 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java @@ -15,14 +15,9 @@ */ package org.linguafranca.pwdb; -import org.linguafranca.pwdb.checks.BasicDatabaseChecks; import org.linguafranca.pwdb.checks.PropertyValueChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; -import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; -import org.linguafranca.pwdb.kdbx.jackson.JacksonGroup; -import org.linguafranca.pwdb.kdbx.jackson.JacksonIcon; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; import java.io.IOException; import java.io.InputStream; @@ -40,8 +35,8 @@ public void saveDatabase(Database database, Credentials credentials, OutputStrea } @Override - public JacksonDatabase loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return JacksonDatabase.load(credentials, inputStream); + public KdbxDatabase loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { + return KdbxDatabase.load(credentials, inputStream); } @Override @@ -49,8 +44,8 @@ public Credentials getCreds(byte[] creds) { return new KdbxCreds(creds); } @Override - public JacksonDatabase createDatabase() throws IOException { - return new JacksonDatabase(); + public KdbxDatabase createDatabase() throws IOException { + return new KdbxDatabase(); } } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java similarity index 89% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java index fc63749d..5a0123f1 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java @@ -16,13 +16,13 @@ package org.linguafranca.pwdb; import org.linguafranca.pwdb.checks.RecycleBinChecks; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; public class JacksonRecycleBinTest extends RecycleBinChecks { public JacksonRecycleBinTest() { try { - database = new JacksonDatabase(); + database = new KdbxDatabase(); } catch(Exception e) { e.printStackTrace(); } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java similarity index 66% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java index eb79badd..5a7688d3 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java @@ -16,35 +16,24 @@ package org.linguafranca.pwdb; import static org.junit.Assert.assertTrue; -import static org.linguafranca.util.TestUtil.getTestPrintStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.OutputStream; -import java.io.PrintStream; -import org.junit.Test; import org.linguafranca.pwdb.checks.SaveAndReloadChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; -import org.linguafranca.pwdb.kdbx.jackson.JacksonEntry; -import org.linguafranca.pwdb.kdbx.jackson.JacksonGroup; -import org.linguafranca.pwdb.kdbx.jackson.JacksonIcon; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; public class JacksonSaveAndReloadTest extends SaveAndReloadChecks{ @Override - public JacksonDatabase getDatabase() { + public KdbxDatabase getDatabase() { try { - return new JacksonDatabase(); + return new KdbxDatabase(); } catch(Exception e) { return null; } @@ -52,9 +41,9 @@ public JacksonDatabase getDatabase() { } @Override - public JacksonDatabase getDatabase(String name, Credentials credentials) throws IOException { + public KdbxDatabase getDatabase(String name, Credentials credentials) throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name); - return JacksonDatabase.load(credentials, inputStream); + return KdbxDatabase.load(credentials, inputStream); } @Override @@ -63,8 +52,8 @@ public void saveDatabase(Database database, Credentials credentials, OutputStrea } @Override - public JacksonDatabase loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return JacksonDatabase.load(credentials, inputStream); + public KdbxDatabase loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { + return KdbxDatabase.load(credentials, inputStream); } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java similarity index 78% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoadTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java index 2f0e4d2e..7cf9e5a8 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java @@ -19,12 +19,12 @@ import java.io.PrintStream; import org.junit.Test; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; import static org.linguafranca.util.TestUtil.getTestPrintStream; -public class JacksonDatabaseLoadTest { +public class KdbxDatabaseLoadTest { static PrintStream printStream = getTestPrintStream(); @@ -32,20 +32,20 @@ public class JacksonDatabaseLoadTest { @Test public void loadXml() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("ExampleDatabase.xml"); - JacksonDatabase database = JacksonDatabase.loadXml(inputStream); + KdbxDatabase database = KdbxDatabase.loadXml(inputStream); database.visit(new Visitor.Print(printStream)); } @Test public void loadKdbx() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - JacksonDatabase database = JacksonDatabase.load(new KdbxCreds("123".getBytes()), inputStream); + KdbxDatabase database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); database.visit(new Visitor.Print(printStream)); } @Test public void loadKdbxV4() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); - JacksonDatabase database = JacksonDatabase.load(new KdbxCreds("123".getBytes()), inputStream); + KdbxDatabase database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); database.visit(new Visitor.Print(printStream)); // test what happens to dates in V4 database.visit(new Visitor.Default(){ @@ -58,14 +58,14 @@ public void visit(Entry entry) { @Test public void emptyDb() throws Exception { - JacksonDatabase database = new JacksonDatabase(); + KdbxDatabase database = new KdbxDatabase(); printStream.println(database.getDescription()); } @Test public void dbWithDeleted() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("testDeleted.kdbx"); - JacksonDatabase database = JacksonDatabase.load(new KdbxCreds("123".getBytes()), inputStream); + KdbxDatabase database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); database.visit(new Visitor.Print(printStream)); } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoaderTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java similarity index 75% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoaderTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java index 7c218fc3..4b0d323b 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseLoaderTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java @@ -18,17 +18,17 @@ import java.io.InputStream; import org.linguafranca.pwdb.checks.DatabaseLoaderChecks; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; -public class JacksonDatabaseLoaderTest extends DatabaseLoaderChecks{ +public class KdbxDatabaseLoaderTest extends DatabaseLoaderChecks{ - public JacksonDatabaseLoaderTest() throws Exception{ + public KdbxDatabaseLoaderTest() throws Exception{ InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); // file has password credentials Credentials credentials = new KdbxCreds("123".getBytes()); - super.database = JacksonDatabase.load(credentials, inputStream); + super.database = KdbxDatabase.load(credentials, inputStream); } } diff --git a/jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java similarity index 79% rename from jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java index cbaaa291..40c2f5e4 100644 --- a/jackson/src/test/java/org/linguafranca/pwdb/JacksonDatabaseTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java @@ -18,18 +18,18 @@ import java.io.IOException; import org.linguafranca.pwdb.checks.BasicDatabaseChecks; -import org.linguafranca.pwdb.kdbx.jackson.JacksonDatabase; +import org.linguafranca.pwdb.kdbx.KdbxDatabase; -public class JacksonDatabaseTest extends BasicDatabaseChecks { +public class KdbxDatabaseTest extends BasicDatabaseChecks { - public JacksonDatabaseTest() throws IOException { + public KdbxDatabaseTest() throws IOException { } @Override public Database createDatabase() throws IOException { - return new JacksonDatabase(); + return new KdbxDatabase(); } } diff --git a/kdbx/pom.xml b/kdbx-io/pom.xml similarity index 97% rename from kdbx/pom.xml rename to kdbx-io/pom.xml index 414f71bb..68f6e691 100644 --- a/kdbx/pom.xml +++ b/kdbx-io/pom.xml @@ -24,7 +24,7 @@ 4.0.0 - KeePassJava2-kdbx + KeePassJava2-kdbx-io KeePassJava2 :: KDBX Base classes and resources for other KDBX modules. KDBX Stream Support. diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/Helpers.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java similarity index 99% rename from kdbx/src/main/java/org/linguafranca/pwdb/kdbx/Helpers.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java index d789bd15..8285e8e9 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/Helpers.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.format; import com.google.common.io.ByteStreams; import org.apache.commons.codec.binary.Base64; diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxCredentials.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java similarity index 98% rename from kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxCredentials.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java index 3863dbee..a21aefb2 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxCredentials.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.format; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.security.Encryption; diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxCreds.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java similarity index 98% rename from kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxCreds.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java index dff3d242..eac69171 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxCreds.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.format; import org.jetbrains.annotations.NotNull; import org.linguafranca.pwdb.Credentials; diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHeader.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxHeader.java similarity index 99% rename from kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHeader.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxHeader.java index ecfdb585..fb4e490e 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHeader.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxHeader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.format; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.StreamConfiguration; @@ -125,7 +125,7 @@ interface KdbxHeaderOptions { /** * Default values for crypto options */ - enum KdbxHeaderOpts implements KdbxHeaderOptions{ + public enum KdbxHeaderOpts implements KdbxHeaderOptions{ V3_AES_SALSA_20(3, Encryption.Cipher.AES, Encryption.KeyDerivationFunction.AES, Encryption.ProtectedStreamAlgorithm.SALSA_20), V4_AES_ARGON_CHA_CHA (4, Encryption.Cipher.AES, Encryption.KeyDerivationFunction.ARGON2, Encryption.ProtectedStreamAlgorithm.CHA_CHA_20); diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxKeyFile.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxKeyFile.java similarity index 99% rename from kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxKeyFile.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxKeyFile.java index c3f7004e..33093aca 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxKeyFile.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxKeyFile.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.format; import org.apache.commons.codec.DecoderException; import org.apache.commons.codec.binary.Base64; diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializer.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java similarity index 99% rename from kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializer.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java index 98f36eab..5a443231 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializer.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.format; import com.google.common.io.LittleEndianDataInputStream; import com.google.common.io.LittleEndianDataOutputStream; diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxStreamFormat.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java similarity index 99% rename from kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxStreamFormat.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java index c6d3c730..66039456 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/kdbx/KdbxStreamFormat.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.format; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.SerializableDatabase; diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java similarity index 100% rename from kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java similarity index 100% rename from kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java similarity index 100% rename from kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java similarity index 100% rename from kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java similarity index 94% rename from kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java index 9a7825df..b08c0bad 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java @@ -2,13 +2,14 @@ import com.google.common.io.LittleEndianDataInputStream; import org.jetbrains.annotations.NotNull; +import org.linguafranca.pwdb.format.Helpers; import javax.crypto.Mac; import java.io.*; import java.nio.ByteOrder; import java.util.Arrays; -import static org.linguafranca.pwdb.kdbx.Helpers.toBytes; +import static org.linguafranca.pwdb.format.Helpers.toBytes; import static org.linguafranca.pwdb.security.Encryption.getHMacSha256Instance; import static org.linguafranca.pwdb.security.Encryption.transformHmacKey; @@ -99,10 +100,10 @@ private void getBlock() throws IOException { * @param hmacSha256 the hmac to verify */ private void verifyHmac(byte[] buffer, long blockNumber, byte[] hmacSha256) { - final byte[] transformedKey = transformHmacKey(this.key, toBytes(blockNumber, byteOrder)); + final byte[] transformedKey = transformHmacKey(this.key, Helpers.toBytes(blockNumber, byteOrder)); final Mac mac = getHMacSha256Instance(transformedKey); - mac.update(toBytes(blockNumber, byteOrder)); - mac.update(toBytes(buffer.length, byteOrder)); + mac.update(Helpers.toBytes(blockNumber, byteOrder)); + mac.update(Helpers.toBytes(buffer.length, byteOrder)); if (!Arrays.equals(mac.doFinal(buffer), hmacSha256)) { throw new IllegalStateException("Block HMAC does not match"); } diff --git a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java similarity index 94% rename from kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java rename to kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java index 5dd0d252..14b4fa64 100644 --- a/kdbx/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java @@ -1,6 +1,6 @@ package org.linguafranca.pwdb.hashedblock; -import static org.linguafranca.pwdb.kdbx.Helpers.toBytes; +import static org.linguafranca.pwdb.format.Helpers.toBytes; import static org.linguafranca.pwdb.security.Encryption.getHMacSha256Instance; import static org.linguafranca.pwdb.security.Encryption.transformHmacKey; @@ -9,6 +9,7 @@ import javax.crypto.Mac; import org.jetbrains.annotations.NotNull; import com.google.common.io.LittleEndianDataOutputStream; +import org.linguafranca.pwdb.format.Helpers; /** * Takes a stream of (encrypted GZIPped) data and formats it as HMAC Hashed Blocks to the @@ -83,10 +84,10 @@ public void close() throws IOException { } private void writeBlock(byte [] buffer) throws IOException { - final byte[] transformedKey = transformHmacKey(this.key, toBytes(nextSequenceNumber, byteOrder)); + final byte[] transformedKey = transformHmacKey(this.key, Helpers.toBytes(nextSequenceNumber, byteOrder)); final Mac mac = getHMacSha256Instance(transformedKey); - mac.update(toBytes(nextSequenceNumber++, byteOrder)); - mac.update(toBytes(buffer.length, byteOrder)); + mac.update(Helpers.toBytes(nextSequenceNumber++, byteOrder)); + mac.update(Helpers.toBytes(buffer.length, byteOrder)); mac.update(buffer); byte[] finalMac = mac.doFinal(); output.write(finalMac); diff --git a/kdbx/src/main/resources/base.kdbx.xml b/kdbx-io/src/main/resources/base.kdbx.xml similarity index 100% rename from kdbx/src/main/resources/base.kdbx.xml rename to kdbx-io/src/main/resources/base.kdbx.xml diff --git a/kdbx/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java similarity index 100% rename from kdbx/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java rename to kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java diff --git a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java similarity index 97% rename from kdbx/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java rename to kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java index 586bcf81..1205d30d 100644 --- a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java @@ -17,6 +17,7 @@ package org.linguafranca.pwdb.kdbx; import org.junit.Test; +import org.linguafranca.pwdb.format.Helpers; import java.io.PrintStream; import java.util.UUID; diff --git a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java similarity index 94% rename from kdbx/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java rename to kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java index c4ad11aa..570f005f 100644 --- a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java @@ -1,16 +1,16 @@ package org.linguafranca.pwdb.kdbx; import org.junit.Test; +import org.linguafranca.pwdb.format.Helpers; import java.io.PrintStream; import java.text.ParseException; import java.time.Instant; -import java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Date; import static org.junit.Assert.*; -import static org.linguafranca.pwdb.kdbx.Helpers.dateTimeFormatter; +import static org.linguafranca.pwdb.format.Helpers.dateTimeFormatter; import static org.linguafranca.util.TestUtil.getTestPrintStream; public class HelpersTest { diff --git a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java similarity index 95% rename from kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java rename to kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java index f6a3b440..58c7a965 100644 --- a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java @@ -4,10 +4,10 @@ import com.google.common.io.LittleEndianDataInputStream; import org.junit.Ignore; import org.junit.Test; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; import org.linguafranca.pwdb.hashedblock.HmacBlockInputStream; -import org.linguafranca.pwdb.kdbx.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxSerializer; import java.io.IOException; import java.io.InputStream; diff --git a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java similarity index 97% rename from kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java rename to kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java index 31b8801f..603a93a7 100644 --- a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java @@ -19,6 +19,10 @@ import com.google.common.io.CharStreams; import org.junit.Test; import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxKeyFile; +import org.linguafranca.pwdb.format.KdbxSerializer; import java.io.IOException; import java.io.InputStream; diff --git a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java similarity index 96% rename from kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java rename to kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java index 27d8011d..4a93498c 100644 --- a/kdbx/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java @@ -19,6 +19,9 @@ import com.google.common.io.CharStreams; import org.junit.Test; import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; import java.io.*; import java.nio.charset.StandardCharsets; diff --git a/kdbx/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java similarity index 100% rename from kdbx/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java rename to kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java diff --git a/pom.xml b/pom.xml index 9587873e..a274f288 100644 --- a/pom.xml +++ b/pom.xml @@ -25,8 +25,8 @@ database test kdb - kdbx - jackson + kdbx-io + kdbx-database all example diff --git a/readme.md b/readme.md index fa4bcaca..d49e994e 100644 --- a/readme.md +++ b/readme.md @@ -3,10 +3,11 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.linguafranca.pwdb/KeePassJava2-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.linguafranca.pwdb/KeePassJava2-parent) [![javadoc](https://javadoc.io/badge2/org.linguafranca.pwdb/KeePassJava2/javadoc.svg)](https://javadoc.io/doc/org.linguafranca.pwdb/KeePassJava2) -![alt text](https://badgen.net/badge/Build/2.2.2/blue?icon=github) -master [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/master) -develop [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) +![alt text](https://badgen.net/badge/Branch/master/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.2/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/master) +![alt text](https://badgen.net/badge/Branch/develop/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.3-SNAPSHOT/blue?icon=github)[![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) + +![alt text](https://badgen.net/badge/Branch/v3/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.3-SNAPSHOT/blue?icon=github)[![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/v3) A Java 8 API for databases compatible with the renowned [KeePass](http://keepass.info) password @@ -18,10 +19,11 @@ Features to date: - Read and write KeePass 2.x format (KDBX file formats V3 and V4) - Keepass 2.x Password and Keyfile Credentials -- Read KeePass 1.x format (Rijndael only) +- Read KeePass 1.x format (KDB format, Rijndael only) - *No* requirement for JCE Policy Files - Android compatible - Interfaces for Database, Group and Entry allow compatible addition of other formats +- Pluggable memory storage and protection strategy It is licensed under the Apache 2 License and is currently usable. @@ -39,7 +41,9 @@ It is licensed under the Apache 2 License and is currently usable. ## Current Status -The current code is version 2.2.2 - released to Maven September 2024. This is on the main branch. See [Build from Source](#build-from-source) +This version is 2.3-SNAPSHOT + +The current released code is version 2.2.2 - released to Maven September 2024. This is on the main branch. See [Build from Source](#build-from-source) Key updates relative to 2.1: - Java 8 (dependencies no longer support Java 7) @@ -64,7 +68,7 @@ at Maven Central. Note that the artifactId has become Camel Case from release 2. ### Snapshot -Snapshot builds are erratically available at [Sonatype](https://oss.sonatype.org/content/repositories/snapshots/org/linguafranca/pwdb/): +Snapshot builds are erratically available at [Sonatype](https://oss.sonatype.org/content/repositories/snapshots/org/linguafranca/pwdb/) e.g.: org.linguafranca.pwdb KeePassJava2 @@ -99,22 +103,19 @@ Create credentials and an input stream for the password vault in question: KdbxCreds creds = new KdbxCreds("123".getBytes()); InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test1.kdbx"); -then choose the Jackson based database implementation, and load the database +then load the database: - Database database = JacksonDatabase.load(credentials, inputStream) + Database database = KdbxDatabase.load(credentials, inputStream) -See below for discussion of other database implementations. Note that they will not be -maintained in the future - and see the following regarding making the storage of -passwords more secure. +> In the past there were a number of different database implementations, at present there +are two, one for KDBX (`KdbxDatabase` - previously called `JacksonDatabase`, because it uses Jackson for XML serialization) +and one to support the KeePass V2 KDB format (`KdbDatabase`). ### Storing Passwords There are numerous well-understood problems with storing passwords as Strings in Java. See [this discussion](./PropertyValueProtection.md) about the -KeePassJava2 approach to storing passwords. - -> Available from release 2.2.3 the enhanced **Jackson implementation** is the only KeePassJava2 database implementation that provides a -means of storing passwords other than as String. +KeePassJava2 approach to storing passwords, which is implemented in this release. ### Discussion @@ -175,42 +176,20 @@ KeePass formats in the following locations: KeePass - or more specifically its file format KDBX - is an XML based format, so one of the main tasks is serializing and deserializing XML. Over time (KeePassJava2 was originally released in 2014) approaches to Java and XML have been a bit mysterious. However, Jackson has now been chosen as the -underlying framework for implementation of KeePassJava2. +underlying framework for implementation of KeePassJava2. From 2.2.3 a single KDBX implementation is available. There are several other database implementations which will be maintained for bug-fix purposes only, with a view to being withdrawn, since they perform badly and/or depend on obsolete technology. -- `SimpleXML` - no longer maintained, does not work with Java 17 and up -- `JAXB` - this causes problems with `javax` and `jakarta` namespaces, it's not worth maintaining - as it offers no compelling performance or other advantage -- `DOM` the was the original implementation and validates the fact that DOM based implementations - are slow. That said, if you want to load a database and then save it while maintaining whatever quirks - existed in the original database then this is the one. - -Aside from dependencies on underlying frameworks, different implementations have varying characteristics, primarily speed. This is assessed -by [this test](https://github.com/jorabin/KeePassJava2/blob/master/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java) in the module `examples`. - ## Dependencies -Aside from the JRE, at release 2.2, the API depends on: +Aside from the JRE, at release 2.3, the API depends on: - [Google Guava](https://github.com/google/guava/wiki) ([Apache 2 license](https://github.com/google/guava/blob/master/COPYING)). - [Apache Commons Codec](https://commons.apache.org/proper/commons-codec/) ([Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)). - [Bouncy Castle](https://github.com/bcgit/bc-java/blob/master/LICENSE.html) ([MIT License](https://github.com/bcgit/bc-java/blob/master/LICENSE.html)). - -The Jackson implementation depends on: - - [Faster XML Jackson](https://github.com/FasterXML/jackson) -The (historical) Simple XML implementation additionally depends on: - -- [Simple XML Serialisation Framework](http://simple.sourceforge.net/) ([Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)). -- [Faster XML Aalto](https://github.com/FasterXML/aalto-xml) ([Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0.txt)). - -For Java 11 and later, Jaxb implementation depends on explicit inclusion [no longer provided by JDK](https://docs.oracle.com/en/java/javase/11/migrate/index.html#JSMIG-GUID-F640FA9D-FB66-4D85-AD2B-D931174C09A3) of: - -- [JAXB](https://javaee.github.io/jaxb-v2/) - It also depends on SLF4J and Junit 4 for tests. ## Build from Source @@ -252,44 +231,18 @@ Each module corresponds to a Maven artifact. The GroupId is `org.linguafranca.pw Javadocs An implementation of the Database APIs supporting KeePass KDB format. -kdbxKeePassJava2-kdbx -Javadocs +kdbx-ioKeePassJava2-kdbx-io +Javadocs Provides support for KDBX streaming and security. -jacksonKeePassJava2-jackson -Javadocs -A Jackson based implementation of KDBX. Intended to provide the main support going forward, replacing trhe -modules below. - -simpleKeePassJava2-simple -Javadocs -A Simple XML Platform implementation of KDBX. Could be useful for Android. -Simple cannot be used with Java versions 17 and up. - -jaxbKeePassJava2-jaxb -Javadocs -A JAXB implementation of KDBX. Probably not useful for Android. The generated class -bindings might be useful for building other interfaces. - -domKeePassJava2-dom -Javadocs -A DOM based implementation of KDBX. Being DOM based it is rather slow, but -messes less with existing content than the other implementations. Known to work on Android. +kdbx-databaseKeePassJava2-kdbx-database +Javadocs +Provides support for KDBX data access and memory protection. -Why are there so many implementations for KDBX? Well, the DOM implementation came first, because it can -load and save stuff that the implementation doesn't specifically know about. But it is very slow. - -Then came the JAXB implementation, but -belatedly it seems that Android support is in question. So latterly the Simple implementation. That was probably enough -KDBX implementations, however, the Simple XML library seems no longer to be maintained, -and along comes the Jackson Implementation. - -Jackson is the implementation going forward. - ### Gradle If you prefer Gradle the automatic conversion `gradle init` has been known to convert the POM successfully, however you will From fe8906ba087048f9842ea1412044397124fce92e Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 13 Sep 2024 17:38:03 +0100 Subject: [PATCH 06/66] Update readme.md --- readme.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index d49e994e..94102c11 100644 --- a/readme.md +++ b/readme.md @@ -41,14 +41,17 @@ It is licensed under the Apache 2 License and is currently usable. ## Current Status -This version is 2.3-SNAPSHOT +This version is 2.3-SNAPSHOT and is not backwards compatible with 2.2 and earlier versions. The current released code is version 2.2.2 - released to Maven September 2024. This is on the main branch. See [Build from Source](#build-from-source) -Key updates relative to 2.1: +Key updates relative to 2.1 - Java 8 (dependencies no longer support Java 7) - File format version 4 support - with Argon2 -- Inclusion of Jackson based KDBX support with a view to removing SimpleXML, JAXB and JAXB support +- Removal of SimpleXML, JAXB and JAXB database implementations +- Implemented pluggable (protected) data storage model +- Removed generics on database classes +- Refactor modules and packages - Updated keyfile support - Updated dependencies From aadf62dd595ac4a3c8cb69cdbc749b0b6757a96d Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sat, 14 Sep 2024 11:53:02 +0100 Subject: [PATCH 07/66] Tweaks to readme.md --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 94102c11..33de6381 100644 --- a/readme.md +++ b/readme.md @@ -5,9 +5,9 @@ ![alt text](https://badgen.net/badge/Branch/master/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.2/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/master) -![alt text](https://badgen.net/badge/Branch/develop/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.3-SNAPSHOT/blue?icon=github)[![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) +![alt text](https://badgen.net/badge/Branch/develop/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.3-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) -![alt text](https://badgen.net/badge/Branch/v3/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.3-SNAPSHOT/blue?icon=github)[![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/v3) +![alt text](https://badgen.net/badge/Branch/v3/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.3-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/v3) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/fe9059ac4d384b929f452149b9246658)](https://app.codacy.com/gh/jorabin/KeePassJava2/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) A Java 8 API for databases compatible with the renowned [KeePass](http://keepass.info) password From d8f444ffe17016c7cb3770ef2b496f9c11ca1d35 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 23 Sep 2024 20:06:30 +0100 Subject: [PATCH 08/66] updating versions to 3.0.0-SNAPSHOT update java version to 11 --- all/pom.xml | 2 +- database/pom.xml | 2 +- example/pom.xml | 2 +- kdb/pom.xml | 2 +- kdbx-database/pom.xml | 2 +- kdbx-io/pom.xml | 2 +- pom.xml | 4 ++-- readme.md | 14 +++++++------- test/pom.xml | 2 +- 9 files changed, 16 insertions(+), 16 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index e59dcefc..4506a90e 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -19,7 +19,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml diff --git a/database/pom.xml b/database/pom.xml index 1feefcb8..20176b5c 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -3,7 +3,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/example/pom.xml b/example/pom.xml index b0bdc257..587cffc4 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -19,7 +19,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/kdb/pom.xml b/kdb/pom.xml index 46f163c3..6e84458d 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -3,7 +3,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index faa622df..23088415 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -18,7 +18,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 68f6e691..3c286552 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -19,7 +19,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT ../pom.xml 4.0.0 diff --git a/pom.xml b/pom.xml index a274f288..04d41bae 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ org.linguafranca.pwdb KeePassJava2-parent - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT database test @@ -64,7 +64,7 @@ UTF-8 - 1.8 + 11 3.11.0 3.0.0 diff --git a/readme.md b/readme.md index 33de6381..d0d21c4d 100644 --- a/readme.md +++ b/readme.md @@ -41,7 +41,7 @@ It is licensed under the Apache 2 License and is currently usable. ## Current Status -This version is 2.3-SNAPSHOT and is not backwards compatible with 2.2 and earlier versions. +This version is 3.0.0-SNAPSHOT and is not backwards compatible with 2.2 and earlier versions. The current released code is version 2.2.2 - released to Maven September 2024. This is on the main branch. See [Build from Source](#build-from-source) @@ -215,15 +215,15 @@ Each module corresponds to a Maven artifact. The GroupId is `org.linguafranca.pw databasedatabase -Javadocs +Javadocs Base definition of the Database APIs. exampleexample -Javadocs +Javadocs Worked examples of loading, saving, splicing etc. using the APIs testtest -Javadocs +Javadocs Shared tests to assess the viability of the implementation. allKeePassJava2 @@ -231,16 +231,16 @@ Each module corresponds to a Maven artifact. The GroupId is `org.linguafranca.pw This is the main KeePassJava2 Maven dependency. Provides a route to all artifacts (other than test and examples) via transitive dependency. kdbKeePassJava2-kdb -Javadocs +Javadocs An implementation of the Database APIs supporting KeePass KDB format. kdbx-ioKeePassJava2-kdbx-io -Javadocs +Javadocs Provides support for KDBX streaming and security. kdbx-databaseKeePassJava2-kdbx-database -Javadocs +Javadocs Provides support for KDBX data access and memory protection. diff --git a/test/pom.xml b/test/pom.xml index 07875d5e..28ec611a 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -3,7 +3,7 @@ KeePassJava2-parent org.linguafranca.pwdb - 2.3-SNAPSHOT + 3.0.0-SNAPSHOT 4.0.0 From 3d8e722c2f9aebff1d9653fb562f6429591d1e3f Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 23 Sep 2024 22:32:34 +0100 Subject: [PATCH 09/66] Update circleci to remove the Java 8 compile --- .circleci/config.yml | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5151a953..1890bf8f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,26 +6,6 @@ version: 2.1 # See: https://circleci.com/docs/2.0/configuration-reference/#jobs jobs: # Below is the definition of your job to build and test your app, you can rename and customize it as you want. - build-and-test-8: - # These next lines define a Docker executor: https://circleci.com/docs/2.0/executor-types/ - # You can specify an image from Dockerhub or use one of our Convenience Images from CircleCI's Developer Hub. - # Be sure to update the Docker image tag below to openjdk version of your application. - # A list of available CircleCI Docker Convenience Images are available here: https://circleci.com/developer/images/image/cimg/openjdk - docker: - - image: cimg/openjdk:8.0 - # Add steps to the job - # See: https://circleci.com/docs/2.0/configuration-reference/#steps - steps: - # Checkout the code as the first step. - - checkout - # Use mvn clean and package as the standard maven build phase - - run: - name: Build - command: mvn -B -DskipTests clean package - # Then run your tests! - - run: - name: Test - command: mvn -P inhibitConsoleOutput test build-and-test-11: # These next lines define a Docker executor: https://circleci.com/docs/2.0/executor-types/ @@ -51,8 +31,7 @@ jobs: # Invoke jobs via workflows # See: https://circleci.com/docs/2.0/configuration-reference/#workflows workflows: - build-8-11: # This is the name of the workflow, feel free to change it to better match your workflow. + build-11: # This is the name of the workflow, feel free to change it to better match your workflow. # Inside the workflow, you define the jobs you want to run. jobs: - - build-and-test-8 - build-and-test-11 From db4cafc4139346184d0a57af36e5a2f10e6abb4f Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 24 Sep 2024 11:50:07 +0100 Subject: [PATCH 10/66] Refactor package names --- .../main/java/org/linguafranca/{pwdb => all}/IgnoreMe.java | 2 +- .../java/org/linguafranca/{pwdb => all}/package-info.java | 4 ++-- .../main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java | 1 + .../src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java | 1 + example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java | 1 + .../java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java | 1 + .../org/linguafranca/pwdb/kdbx/validation/Issue27Test.java | 2 +- .../org/linguafranca/pwdb/kdbx/validation/Issue33Test.java | 2 +- .../org/linguafranca/pwdb/kdbx/validation/Issue38Test.java | 2 +- .../linguafranca/pwdb/kdbx/{ => jackson}/KdbxDatabase.java | 4 ++-- .../org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxEntry.java | 2 +- .../org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxGroup.java | 2 +- .../org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxHistory.java | 2 +- .../org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxIcon.java | 2 +- .../pwdb/kdbx/{ => jackson}/KdbxSerializableDatabase.java | 2 +- .../linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java | 2 +- .../org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java | 2 +- .../org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java | 2 +- .../org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java | 2 +- .../java/org/linguafranca/pwdb/JacksonPropertyValueTest.java | 2 +- .../java/org/linguafranca/pwdb/JacksonRecycleBinTest.java | 2 +- .../java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java | 2 +- .../test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java | 2 +- .../java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java | 2 +- .../src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java | 2 +- 25 files changed, 27 insertions(+), 23 deletions(-) rename all/src/main/java/org/linguafranca/{pwdb => all}/IgnoreMe.java (73%) rename all/src/main/java/org/linguafranca/{pwdb => all}/package-info.java (68%) rename kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxDatabase.java (97%) rename kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxEntry.java (99%) rename kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxGroup.java (99%) rename kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxHistory.java (96%) rename kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxIcon.java (96%) rename kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/{ => jackson}/KdbxSerializableDatabase.java (99%) diff --git a/all/src/main/java/org/linguafranca/pwdb/IgnoreMe.java b/all/src/main/java/org/linguafranca/all/IgnoreMe.java similarity index 73% rename from all/src/main/java/org/linguafranca/pwdb/IgnoreMe.java rename to all/src/main/java/org/linguafranca/all/IgnoreMe.java index 0f2521c5..463fbdbe 100644 --- a/all/src/main/java/org/linguafranca/pwdb/IgnoreMe.java +++ b/all/src/main/java/org/linguafranca/all/IgnoreMe.java @@ -1,4 +1,4 @@ -package org.linguafranca.pwdb; +package org.linguafranca.all; /** * This is here for Javadoc generation purposes only diff --git a/all/src/main/java/org/linguafranca/pwdb/package-info.java b/all/src/main/java/org/linguafranca/all/package-info.java similarity index 68% rename from all/src/main/java/org/linguafranca/pwdb/package-info.java rename to all/src/main/java/org/linguafranca/all/package-info.java index a544df96..83bf3905 100644 --- a/all/src/main/java/org/linguafranca/pwdb/package-info.java +++ b/all/src/main/java/org/linguafranca/all/package-info.java @@ -1,9 +1,9 @@ /** - * This module provides a simple import of the database implementations of KeePass, "all" in the diagram below. + * This package/module provides a simple import of the database implementations of KeePass, "all" in the diagram below. *

* *

* @see Module Structure * in the readme at GitHub for a discussion of the project modules and links to JavaDocs. */ -package org.linguafranca.pwdb; \ No newline at end of file +package org.linguafranca.all; \ No newline at end of file diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index f95d2074..f05c9a4a 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -3,6 +3,7 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Visitor; import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.InputStream; import java.io.PrintStream; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 0ab4a0c6..338080e6 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -23,6 +23,7 @@ import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdb.KdbCredentials; import org.linguafranca.pwdb.kdb.KdbDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.security.Encryption; import java.io.*; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java index 62f34d1a..f6639086 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java @@ -7,6 +7,7 @@ import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.*; import java.nio.charset.StandardCharsets; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index a223c71c..be4001de 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -20,6 +20,7 @@ import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.format.KdbxStreamFormat; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.security.Encryption; import java.io.IOException; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java index a385dc88..9c0f7b01 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java @@ -8,7 +8,7 @@ import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.IOException; import java.io.InputStream; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java index b732aef4..d5f65d65 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java @@ -9,7 +9,7 @@ import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.kdbx.Util; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.*; import java.nio.file.Files; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java index 777f1607..339c7b5e 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java @@ -3,7 +3,7 @@ import org.junit.Test; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.IOException; import java.io.InputStream; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java similarity index 97% rename from kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxDatabase.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index 476ce8a1..475b43f1 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.jackson; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.StreamConfiguration; @@ -32,7 +32,7 @@ import java.util.Objects; import java.util.UUID; -import static org.linguafranca.pwdb.kdbx.KdbxSerializableDatabase.createEmptyDatabase; +import static org.linguafranca.pwdb.kdbx.jackson.KdbxSerializableDatabase.createEmptyDatabase; public class KdbxDatabase extends ProtectedDatabase { diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java similarity index 99% rename from kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxEntry.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index d870d049..c135b631 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.jackson; import java.util.ArrayList; import java.util.Date; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java similarity index 99% rename from kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxGroup.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 5069826d..2ee79095 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.jackson; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHistory.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxHistory.java similarity index 96% rename from kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHistory.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxHistory.java index a35d78e4..fd2fd91b 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxHistory.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxHistory.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.jackson; import java.util.ArrayList; import java.util.List; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxIcon.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxIcon.java similarity index 96% rename from kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxIcon.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxIcon.java index cf196f1c..c1f34a74 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxIcon.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxIcon.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.jackson; import org.linguafranca.pwdb.Icon; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java similarity index 99% rename from kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializableDatabase.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index b2eb6376..2faf44a0 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.jackson; import com.ctc.wstx.api.WstxInputProperties; import com.ctc.wstx.api.WstxOutputProperties; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java index 580afc70..309cc814 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java @@ -20,7 +20,7 @@ import java.util.List; import org.linguafranca.pwdb.PropertyValue; -import org.linguafranca.pwdb.kdbx.KdbxEntry; +import org.linguafranca.pwdb.kdbx.jackson.KdbxEntry; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.StringToBooleanConverter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java index 6ce1b750..4f7558b8 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java @@ -22,7 +22,7 @@ import java.util.UUID; import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.kdbx.KdbxGroup; +import org.linguafranca.pwdb.kdbx.jackson.KdbxGroup; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToByteConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java index 3ad55d1d..2add540b 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java @@ -22,7 +22,7 @@ import org.linguafranca.pwdb.checks.BinaryPropertyChecks; import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; public class JacksonBinaryPropertyV3Test extends BinaryPropertyChecks { diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java index f738dd41..794ed2a3 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java @@ -21,7 +21,7 @@ import org.linguafranca.pwdb.checks.BinaryPropertyChecks; import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; public class JacksonBinaryPropertyV4Test extends BinaryPropertyChecks{ diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java index 0cdb915a..c605d1b9 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java @@ -17,7 +17,7 @@ import org.linguafranca.pwdb.checks.PropertyValueChecks; import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.IOException; import java.io.InputStream; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java index 5a0123f1..71ed55e1 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java @@ -16,7 +16,7 @@ package org.linguafranca.pwdb; import org.linguafranca.pwdb.checks.RecycleBinChecks; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; public class JacksonRecycleBinTest extends RecycleBinChecks { diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java index 5a7688d3..5f9eea33 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java @@ -25,7 +25,7 @@ import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.format.KdbxHeader; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; public class JacksonSaveAndReloadTest extends SaveAndReloadChecks{ diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java index 7cf9e5a8..91217cc6 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java @@ -20,7 +20,7 @@ import org.junit.Test; import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import static org.linguafranca.util.TestUtil.getTestPrintStream; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java index 4b0d323b..599b29a4 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java @@ -19,7 +19,7 @@ import org.linguafranca.pwdb.checks.DatabaseLoaderChecks; import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; public class KdbxDatabaseLoaderTest extends DatabaseLoaderChecks{ diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java index 40c2f5e4..2ca89590 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java @@ -18,7 +18,7 @@ import java.io.IOException; import org.linguafranca.pwdb.checks.BasicDatabaseChecks; -import org.linguafranca.pwdb.kdbx.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; public class KdbxDatabaseTest extends BasicDatabaseChecks { From d079e5d0e0678661a5a5ea1fa23d7d85f644e523 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 24 Sep 2024 11:51:01 +0100 Subject: [PATCH 11/66] tidy POM, remove automatic module generation --- pom.xml | 31 +++++++++++++++++-------------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 04d41bae..d342e95a 100644 --- a/pom.xml +++ b/pom.xml @@ -33,7 +33,7 @@ pom KeePassJava2 - A Java 8 API for databases compatible with the renowned KeePass password safe for Windows + A Java 11 API for databases compatible with the renowned KeePass password safe for Windows https://github.com/jorabin/KeePassJava2 @@ -76,6 +76,8 @@ 3.0.2 false + 3.6.0 + 3.4.0 @@ -104,18 +106,6 @@ - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - - - ${project.groupId}.${project.artifactId} - - - - @@ -143,8 +133,21 @@ org.apache.maven.plugins maven-dependency-plugin - 3.6.0 + ${maven-dependency-plugin.version} + + maven-clean-plugin + ${maven-clean-plugin.version} + + + + testOutput + false + + + + + From 9a318e33f3caebd637521b49aa324e3eb1d0a021 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 24 Sep 2024 15:31:40 +0100 Subject: [PATCH 12/66] Replace dashes in artefact names with dots and restore automatic module naming --- all/pom.xml | 6 +++--- database/pom.xml | 2 +- example/pom.xml | 2 +- kdb/pom.xml | 4 ++-- kdbx-database/pom.xml | 6 +++--- kdbx-io/pom.xml | 4 ++-- pom.xml | 14 +++++++++++++- test/pom.xml | 2 +- 8 files changed, 26 insertions(+), 14 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 4506a90e..80afdc27 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -17,7 +17,7 @@ - KeePassJava2-parent + KeePassJava2.parent org.linguafranca.pwdb 3.0.0-SNAPSHOT ../pom.xml @@ -32,12 +32,12 @@ org.linguafranca.pwdb - KeePassJava2-kdb + KeePassJava2.kdb ${project.version} org.linguafranca.pwdb - KeePassJava2-kdbx-database + KeePassJava2.kdbx.database ${project.version} diff --git a/database/pom.xml b/database/pom.xml index 20176b5c..1eaf0e20 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -1,7 +1,7 @@ - KeePassJava2-parent + KeePassJava2.parent org.linguafranca.pwdb 3.0.0-SNAPSHOT ../pom.xml diff --git a/example/pom.xml b/example/pom.xml index 587cffc4..5062e9b1 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -17,7 +17,7 @@ - KeePassJava2-parent + KeePassJava2.parent org.linguafranca.pwdb 3.0.0-SNAPSHOT ../pom.xml diff --git a/kdb/pom.xml b/kdb/pom.xml index 6e84458d..78774dbe 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -1,14 +1,14 @@ - KeePassJava2-parent + KeePassJava2.parent org.linguafranca.pwdb 3.0.0-SNAPSHOT ../pom.xml 4.0.0 - KeePassJava2-kdb + KeePassJava2.kdb KeePassJava2 :: KDB Contains an implementaion that can load KDB files. diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index 23088415..9be2385d 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -16,21 +16,21 @@ --> - KeePassJava2-parent + KeePassJava2.parent org.linguafranca.pwdb 3.0.0-SNAPSHOT ../pom.xml 4.0.0 - KeePassJava2-kdbx-database + KeePassJava2.kdbx.database KeePassJava2 :: Jackson Contains a Jackson XML implementation for KDBX. org.linguafranca.pwdb - KeePassJava2-kdbx-io + KeePassJava2.kdbx.io ${project.version} diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 3c286552..89eb663b 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -17,14 +17,14 @@ - KeePassJava2-parent + KeePassJava2.parent org.linguafranca.pwdb 3.0.0-SNAPSHOT ../pom.xml 4.0.0 - KeePassJava2-kdbx-io + KeePassJava2.kdbx.io KeePassJava2 :: KDBX Base classes and resources for other KDBX modules. KDBX Stream Support. diff --git a/pom.xml b/pom.xml index d342e95a..652dbe34 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 4.0.0 org.linguafranca.pwdb - KeePassJava2-parent + KeePassJava2.parent 3.0.0-SNAPSHOT database @@ -106,6 +106,18 @@ + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + ${project.groupId}.${project.artifactId} + + + + diff --git a/test/pom.xml b/test/pom.xml index 28ec611a..3ebff937 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -1,7 +1,7 @@ - KeePassJava2-parent + KeePassJava2.parent org.linguafranca.pwdb 3.0.0-SNAPSHOT From 86ecc28b983c6921676ba1eb798412fffb3b1b3c Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 24 Sep 2024 16:27:33 +0100 Subject: [PATCH 13/66] Updating POM version management --- database/pom.xml | 2 -- kdb/pom.xml | 1 - kdbx-io/pom.xml | 2 -- pom.xml | 56 +++++++++++++++++++++++++++++++++++------------- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/database/pom.xml b/database/pom.xml index 1eaf0e20..8832b7b5 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -16,12 +16,10 @@ org.jetbrains annotations - 24.1.0 com.google.guava guava - 33.2.1-android org.bouncycastle diff --git a/kdb/pom.xml b/kdb/pom.xml index 78774dbe..b11429f2 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -21,7 +21,6 @@ org.jetbrains annotations - 24.1.0 org.linguafranca.pwdb diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 89eb663b..2caf71b9 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -41,12 +41,10 @@ commons-codec commons-codec - 1.17.0 org.jetbrains annotations - 24.1.0 org.linguafranca.pwdb diff --git a/pom.xml b/pom.xml index 652dbe34..d9a21e5e 100644 --- a/pom.xml +++ b/pom.xml @@ -66,18 +66,20 @@ UTF-8 11 - 3.11.0 - 3.0.0 - 1.6.13 - 3.2.1 - 3.5.0 - 3.0.1 - 3.0.0 - 3.0.2 + 3.13.0 + 3.5.0 + 1.7.0 + 3.3.1 + 3.10.0 + 3.2.6 + 3.1.1 + 3.4.2 + 3.8.0 + 3.4.0 + 2.17.1 + 3.5.0 false - 3.6.0 - 3.4.0 @@ -118,6 +120,11 @@ + + org.codehaus.mojo + versions-maven-plugin + ${versions-maven-plugin.version} + @@ -159,7 +166,27 @@ - + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-maven + + enforce + + + + + + 3.6.3 + + + + + + @@ -171,7 +198,6 @@ org.slf4j slf4j-api - 2.0.12 ch.qos.logback @@ -184,7 +210,7 @@ org.jetbrains annotations - 24.0.1 + 24.1.0 @@ -200,7 +226,7 @@ org.slf4j slf4j-api - 2.0.5 + 2.0.12 @@ -212,7 +238,7 @@ commons-codec commons-codec - 1.15 + 1.17.1 junit From 34a5be1011a039482aff229d48fcd822bc75aa74 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Wed, 9 Oct 2024 16:20:24 +0100 Subject: [PATCH 14/66] update readme dependencies --- readme.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index d0d21c4d..13242771 100644 --- a/readme.md +++ b/readme.md @@ -186,14 +186,15 @@ only, with a view to being withdrawn, since they perform badly and/or depend on ## Dependencies -Aside from the JRE, at release 2.3, the API depends on: +Aside from the JRE, at release 3.0.0, the API depends on: - [Google Guava](https://github.com/google/guava/wiki) ([Apache 2 license](https://github.com/google/guava/blob/master/COPYING)). - [Apache Commons Codec](https://commons.apache.org/proper/commons-codec/) ([Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)). - [Bouncy Castle](https://github.com/bcgit/bc-java/blob/master/LICENSE.html) ([MIT License](https://github.com/bcgit/bc-java/blob/master/LICENSE.html)). -- [Faster XML Jackson](https://github.com/FasterXML/jackson) +- [Jackson XML](https://github.com/FasterXML/jackson-dataformat-xml) ([Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)) +- [Woodstox](https://github.com/FasterXML/woodstox) ([Apache 2 license](http://www.apache.org/licenses/LICENSE-2.0)) -It also depends on SLF4J and Junit 4 for tests. +It also depends on SLF4J, logback and Junit 4 for tests. ## Build from Source From 0f12b811c69bf4b8fc82de019c4a20895532cf88 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Wed, 9 Oct 2024 16:22:59 +0100 Subject: [PATCH 15/66] tweaks to property value --- .../src/main/java/org/linguafranca/pwdb/PropertyValue.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java index 221020ff..19111bf3 100644 --- a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java +++ b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java @@ -130,7 +130,7 @@ public Factory newUnprotected() { class StringStore implements PropertyValue { private final String value; private final static PropertyValue.Factory factory = - new PropertyValue.Factory(){ + new PropertyValue.Factory<>(){ @Override public StringStore of(CharSequence aCharSequence) { @@ -195,7 +195,7 @@ class BytesStore implements PropertyValue, Serializable { private final byte[] value; private final static PropertyValue.Factory factory = - new PropertyValue.Factory(){ + new PropertyValue.Factory<>(){ @Override public BytesStore of(CharSequence aCharSequence) { @@ -305,7 +305,7 @@ private byte[] getBytes() { } private final static PropertyValue.Factory factory = - new PropertyValue.Factory(){ + new PropertyValue.Factory<>(){ @Override public SealedStore of(CharSequence aCharSequence) { From 48742ea4f891c5ffccbab2622e4d3553c7ab1047 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 14 Oct 2024 19:54:23 +0100 Subject: [PATCH 16/66] explore fluent builder options --- .../java/org/linguafranca/pwdb/Database.java | 1 + .../java/org/linguafranca/pwdb/Entry.java | 11 ++++--- .../java/org/linguafranca/pwdb/Group.java | 31 +++++++++++++------ .../linguafranca/pwdb/base/AbstractEntry.java | 2 +- .../linguafranca/pwdb/base/AbstractGroup.java | 10 +++++- .../org/linguafranca/pwdb/kdbx/Fluent.java | 22 +++++++++++++ .../org/linguafranca/pwdb/kdb/KdbEntry.java | 3 +- .../pwdb/kdbx/jackson/KdbxEntry.java | 10 +++--- 8 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index c1a4d387..0d95d0c7 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -264,6 +264,7 @@ public interface Database { * Obtain a list of those properties that should be protected by default * @return a list of property names */ + @SuppressWarnings("UnusedReturnValue") List listShouldProtect(); /** diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 8b743b11..1da7d4f9 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -55,12 +55,11 @@ public interface Entry { String STANDARD_PROPERTY_NAME_TITLE = "Title"; String STANDARD_PROPERTY_NAME_NOTES = "Notes"; - List STANDARD_PROPERTY_NAMES = Collections.unmodifiableList(Arrays.asList( - STANDARD_PROPERTY_NAME_USER_NAME, + List STANDARD_PROPERTY_NAMES = List.of(STANDARD_PROPERTY_NAME_USER_NAME, STANDARD_PROPERTY_NAME_PASSWORD, STANDARD_PROPERTY_NAME_URL, STANDARD_PROPERTY_NAME_TITLE, - STANDARD_PROPERTY_NAME_NOTES)); + STANDARD_PROPERTY_NAME_NOTES); /** * Interface to implement when using the {@link #match(Entry.Matcher)} @@ -130,7 +129,7 @@ interface Matcher { * non-standard properties are not supported * @see Database#supportsNonStandardPropertyNames() */ - void setProperty(String name, String value); + Entry setProperty(String name, String value); /** * Sets the value of a property as a property value. The method does not check whether the @@ -144,7 +143,7 @@ interface Matcher { * non-standard properties are not supported * @see Database#supportsNonStandardPropertyNames() */ - void setPropertyValue(String name, PropertyValue value); + Entry setPropertyValue(String name, PropertyValue value); /** * Removes this non-standard property, if it exists. @@ -250,6 +249,7 @@ interface Matcher { * * @return a password */ + @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated String getPassword(); @@ -261,6 +261,7 @@ interface Matcher { * @param pass a password * @deprecated use {@link #setPropertyValue(String, PropertyValue)} */ + @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated void setPassword(String pass); diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index 9696014f..fd9b9d3b 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -77,7 +77,7 @@ public interface Group { * Returns the number of groups that are direct children of this group * *

It's possible that returning a list as in {@link #getGroups()} may incur significantly - * more overhead so use this method if only the count is reuqired + * more overhead so use this method if only the count is required */ int getGroupsCount(); @@ -104,10 +104,10 @@ public interface Group { /** * Removes the group supplied from this group. The group removed - * no longer has a parent and so in not part of the database any more unless + * no longer has a parent and so in no longer part of the database unless * it is re-added to another group. *

- * If the group is incompatible with the databse an exception is thrown. + * If the group is incompatible with the database an exception is thrown. *

* If the group is not present no error is thrown. * @param group the group to remove @@ -125,7 +125,7 @@ public interface Group { * Returns the number of entries in this group * *

It's possible that returning a list as in {@link #getEntries()} may incur significantly - * more overhead so use this method if only the count is reuqired + * more overhead so use this method if only the count is required */ int getEntriesCount(); @@ -135,10 +135,10 @@ public interface Group { * *

Entry match is described under {@link Entry#match(String)} * - *

Note: finding within recycle bin is supported, recusion into recycle bin is inhibited + *

Note: finding within recycle bin is supported, recursion into recycle bin is inhibited * * @param match the text to match - * @param recursive whether to include sub groups in the process + * @param recursive whether to include subgroups in the process * @return a modifiable-by-caller list * @see Entry#match(String) */ @@ -150,10 +150,10 @@ public interface Group { * *

Entry match is described under {@link Entry#match(Entry.Matcher)} * - *

Note: finding within recycle bin is supported, recusion into recycle bin is inhibited + *

Note: finding within recycle bin is supported, recursion into recycle bin is inhibited * - * @param matcher the mathcher to use - * @param recursive whether to include sub groups in the process + * @param matcher the matcher to use + * @param recursive whether to include subgroups in the process * @return a modifiable-by-caller list * @see Entry#match(Entry.Matcher) */ @@ -167,6 +167,19 @@ public interface Group { */ Entry addEntry(Entry entry); + /** + * Adds a new entry to this group + * @return the entry added + */ + Entry addEntry(); + + /** + * Adds a new entry to this group + * @param title the title of the entry to add + * @return the entry added + */ + Entry addEntry(String title); + /** * Remove an entry from this group and hence from the database. * @param entry the entry to remove diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java index 38ebea7f..957599b2 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java @@ -130,7 +130,7 @@ public PropertyValue getPropertyValue(String name) { } @Override - public void setPropertyValue(String name, PropertyValue value) { + public Entry setPropertyValue(String name, PropertyValue value) { throw new UnsupportedOperationException(); } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java index 2caff000..a525a36e 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java @@ -16,7 +16,6 @@ package org.linguafranca.pwdb.base; -import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; @@ -31,6 +30,15 @@ */ public abstract class AbstractGroup implements Group { + public Entry addEntry() { + return getDatabase().newEntry(); + } + + @Override + public Entry addEntry(String title) { + return getDatabase().newEntry(title); + } + @Override public List findGroups(String group) { ArrayList result = new ArrayList<>(); diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java new file mode 100644 index 00000000..1bccac17 --- /dev/null +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java @@ -0,0 +1,22 @@ +package org.linguafranca.pwdb.kdbx; + +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; + +public class Fluent { + + public void fluentExample() { + Database database = new KdbxDatabase(); + database.newGroup("Group 1") + .addEntry() + .setProperty("prop1", "value1") + .setProperty("prop2", "value2") + .setProperty("prop3", "value3") + .getParent() + .addEntry() + .setProperty("prop1", "value1") + .setProperty("prop2", "value2") + .setProperty("prop3", "value3"); + + } +} diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index d1747fd6..20a27340 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -62,7 +62,7 @@ public String getProperty(String name) { } @Override - public void setProperty(String name, String value) { + public Entry setProperty(String name, String value) { switch (name) { case STANDARD_PROPERTY_NAME_USER_NAME: setUsername(value); break; case STANDARD_PROPERTY_NAME_PASSWORD: setPassword(value); break; @@ -71,6 +71,7 @@ public void setProperty(String name, String value) { case STANDARD_PROPERTY_NAME_NOTES: setNotes(value); break; default: throw new UnsupportedOperationException("Cannot set non-standard properties in KDB format"); } + return this; } @Override diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index c135b631..1b3d3e30 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -147,14 +147,15 @@ public String getProperty(String s) { @Override @JsonIgnore - public void setProperty(String s, String s1) { + public KdbxEntry setProperty(String s, String s1) { StringProperty sp = getStringProperty(s, string); if (sp != null) { sp.setValue(database.getPropertyValueStrategy().newUnprotected().of(s1)); - return; + return this; } string.add(new StringProperty(s, database.getPropertyValueStrategy().newUnprotected().of(s1))); touch(); + return this; } @Override @@ -166,14 +167,15 @@ public PropertyValue getPropertyValue(String name) { @Override @JsonIgnore - public void setPropertyValue(String name, PropertyValue value) { + public KdbxEntry setPropertyValue(String name, PropertyValue value) { StringProperty sp = getStringProperty(name, string); if (sp != null) { sp.setValue(value); - return; + return this; } string.add(new StringProperty(name, value)); touch(); + return this; } @Override From 0fddc534000ebc6de6105b117d828861c9a9312d Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 22 Nov 2024 13:19:55 +0000 Subject: [PATCH 17/66] Fixes for #70, Refactor for #71 --- database/pom.xml | 11 +- .../{ => pwdb}/util/PropertyValueUtil.java | 4 +- .../linguafranca/pwdb/PropertyValueTest.java | 10 +- example/pom.xml | 19 +- .../linguafranca/pwdb/kdbx/QuickStart.java | 14 +- .../java/org/linguafranca/pwdb/kdbx/Util.java | 1 + .../SecureCharBufferPropertyValueTest.java | 3 +- .../pwdb/kdbx/SimpleQuickStartTest.java | 6 + .../org/linguafranca/pwdb/kdbx/UtilTest.java | 8 +- .../pwdb/kdbx/validation/Issue70Test.java | 33 + kdb/pom.xml | 5 +- kdbx-database/pom.xml | 11 +- .../pwdb/kdbx/jackson/KdbxEntry.java | 7 +- .../pwdb/kdbx/jackson/KdbxGroup.java | 8 + .../pwdb/kdbx/jackson/model/KeePassFile.java | 25 +- .../pwdb/JacksonSaveAndReloadTest.java | 2 - kdbx-io/pom.xml | 10 +- pom.xml | 11 +- test/pom.xml | 19 + .../java/org/linguafranca/util/TestUtil.java | 17 - .../pwdb/checks/BasicDatabaseChecks.java | 23 +- .../pwdb/checks/BinaryPropertyChecks.java | 13 +- .../pwdb/checks/DatabaseLoaderChecks.java | 16 +- .../pwdb/checks/PropertyValueChecks.java | 4 +- .../pwdb/checks/RecycleBinChecks.java | 1 - .../pwdb/checks/SaveAndReloadChecks.java | 21 +- .../{main => test}/resources/Attachment.kdbx | Bin .../{main => test}/resources/Attachment.xml | 312 ++--- test/src/test/resources/Database-4.1-123.kdbx | Bin 0 -> 2759 bytes .../resources/EmptyPassword.kdbx | Bin .../resources/EmptyPasswordWithKey.kdbx | Bin .../resources/EmptyPasswordWithKey.key | 0 .../resources/ExampleDatabase.kdbx | Bin .../resources/ExampleDatabase.xml | 1164 ++++++++--------- .../resources/KeyFileDatabase.kdbx | Bin .../resources/KeyFileDatabase.key | 18 +- .../resources/NoPasswordWithKey.kdbx | Bin .../resources/NoPasswordWithKey.key | 18 +- .../resources/V3-CustomIcon.kdbx | Bin .../{main => test}/resources/V4-AES-AES.kdbx | Bin .../resources/V4-AES-Argon2-CustomIcon.kdbx | Bin .../resources/V4-AES-Argon2.kdbx | Bin .../resources/V4-ChaCha20-AES.kdbx | Bin .../V4-ChaCha20-Argon2-Attachment.kdbx | Bin .../resources/V4-CustomIcon.kdbx | Bin .../resources/issue-27/bogus-timestamp.kdbx | Bin .../resources/issue-27/bogus-timestamp2.kdbx | Bin .../resources/issue-38/Database/Database.kdbx | Bin .../resources/issue-38/Database/Database.keyx | 0 .../resources/issue-38/Database/README.md | 0 test/src/test/resources/issue-70/test1.kdbx | Bin 0 -> 1749 bytes test/src/test/resources/issue-70/test2.kdbx | Bin 0 -> 2071 bytes test/src/{main => test}/resources/kdb.key | 0 .../resources/kdb_with_random_file.kdbx | Bin .../{main => test}/resources/kdbwithkey.kdb | Bin .../{main => test}/resources/kdbxSample.xml | 0 .../resources/kdbx_hash_test.kdbx | Bin .../resources/kdbx_hash_test.keyx | 22 +- .../resources/kdbx_hash_test_wrong_hash.keyx | 22 +- .../resources/kdbx_keyfile32.kdbx | Bin .../resources/kdbx_keyfile64.kdbx | Bin test/src/{main => test}/resources/keyfile32 | 0 test/src/{main => test}/resources/keyfile64 | 0 .../{main => test}/resources/letter J.jpeg | Bin .../{main => test}/resources/letter L.jpeg | Bin test/src/{main => test}/resources/random_file | Bin test/src/{main => test}/resources/test1.kdbx | Bin .../resources/test123-AES-AES.kdbx | Bin .../resources/test123-AES-Argon2.kdbx | Bin .../resources/test123-ChaCha20-AES.kdbx | Bin .../resources/test123-ChaCha20-Argon2.kdbx | Bin test/src/{main => test}/resources/test123.kdb | Bin .../src/{main => test}/resources/test123.kdbx | Bin .../{main => test}/resources/testDeleted.kdbx | Bin .../resources/xml/V4-AES-AES.xml | 0 .../resources/xml/V4-AES-Argon2.xml | 0 .../resources/xml/V4-ChaCha20-AES.xml | 0 .../xml/V4-ChaCha20-Argon2-Attachment.xml | 0 util/pom.xml | 33 + .../java/org/linguafranca/util/HexViewer.java | 8 - .../linguafranca/util/MavenRunListener.java | 0 .../java/org/linguafranca/util/TestUtil.java | 23 + 82 files changed, 1016 insertions(+), 906 deletions(-) rename database/src/main/java/org/linguafranca/{ => pwdb}/util/PropertyValueUtil.java (94%) create mode 100644 example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java delete mode 100644 test/src/main/java/org/linguafranca/util/TestUtil.java rename test/src/{main => test}/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java (95%) rename test/src/{main => test}/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java (96%) rename test/src/{main => test}/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java (91%) rename test/src/{main => test}/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java (99%) rename test/src/{main => test}/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java (98%) rename test/src/{main => test}/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java (95%) rename test/src/{main => test}/resources/Attachment.kdbx (100%) rename test/src/{main => test}/resources/Attachment.xml (99%) create mode 100644 test/src/test/resources/Database-4.1-123.kdbx rename test/src/{main => test}/resources/EmptyPassword.kdbx (100%) rename test/src/{main => test}/resources/EmptyPasswordWithKey.kdbx (100%) rename test/src/{main => test}/resources/EmptyPasswordWithKey.key (100%) rename test/src/{main => test}/resources/ExampleDatabase.kdbx (100%) rename test/src/{main => test}/resources/ExampleDatabase.xml (99%) rename test/src/{main => test}/resources/KeyFileDatabase.kdbx (100%) rename test/src/{main => test}/resources/KeyFileDatabase.key (95%) rename test/src/{main => test}/resources/NoPasswordWithKey.kdbx (100%) rename test/src/{main => test}/resources/NoPasswordWithKey.key (95%) rename test/src/{main => test}/resources/V3-CustomIcon.kdbx (100%) rename test/src/{main => test}/resources/V4-AES-AES.kdbx (100%) rename test/src/{main => test}/resources/V4-AES-Argon2-CustomIcon.kdbx (100%) rename test/src/{main => test}/resources/V4-AES-Argon2.kdbx (100%) rename test/src/{main => test}/resources/V4-ChaCha20-AES.kdbx (100%) rename test/src/{main => test}/resources/V4-ChaCha20-Argon2-Attachment.kdbx (100%) rename test/src/{main => test}/resources/V4-CustomIcon.kdbx (100%) rename test/src/{main => test}/resources/issue-27/bogus-timestamp.kdbx (100%) rename test/src/{main => test}/resources/issue-27/bogus-timestamp2.kdbx (100%) rename test/src/{main => test}/resources/issue-38/Database/Database.kdbx (100%) rename test/src/{main => test}/resources/issue-38/Database/Database.keyx (100%) rename test/src/{main => test}/resources/issue-38/Database/README.md (100%) create mode 100644 test/src/test/resources/issue-70/test1.kdbx create mode 100644 test/src/test/resources/issue-70/test2.kdbx rename test/src/{main => test}/resources/kdb.key (100%) rename test/src/{main => test}/resources/kdb_with_random_file.kdbx (100%) rename test/src/{main => test}/resources/kdbwithkey.kdb (100%) rename test/src/{main => test}/resources/kdbxSample.xml (100%) rename test/src/{main => test}/resources/kdbx_hash_test.kdbx (100%) rename test/src/{main => test}/resources/kdbx_hash_test.keyx (95%) rename test/src/{main => test}/resources/kdbx_hash_test_wrong_hash.keyx (95%) rename test/src/{main => test}/resources/kdbx_keyfile32.kdbx (100%) rename test/src/{main => test}/resources/kdbx_keyfile64.kdbx (100%) rename test/src/{main => test}/resources/keyfile32 (100%) rename test/src/{main => test}/resources/keyfile64 (100%) rename test/src/{main => test}/resources/letter J.jpeg (100%) rename test/src/{main => test}/resources/letter L.jpeg (100%) rename test/src/{main => test}/resources/random_file (100%) rename test/src/{main => test}/resources/test1.kdbx (100%) rename test/src/{main => test}/resources/test123-AES-AES.kdbx (100%) rename test/src/{main => test}/resources/test123-AES-Argon2.kdbx (100%) rename test/src/{main => test}/resources/test123-ChaCha20-AES.kdbx (100%) rename test/src/{main => test}/resources/test123-ChaCha20-Argon2.kdbx (100%) rename test/src/{main => test}/resources/test123.kdb (100%) rename test/src/{main => test}/resources/test123.kdbx (100%) rename test/src/{main => test}/resources/testDeleted.kdbx (100%) rename test/src/{main => test}/resources/xml/V4-AES-AES.xml (100%) rename test/src/{main => test}/resources/xml/V4-AES-Argon2.xml (100%) rename test/src/{main => test}/resources/xml/V4-ChaCha20-AES.xml (100%) rename test/src/{main => test}/resources/xml/V4-ChaCha20-Argon2-Attachment.xml (100%) create mode 100644 util/pom.xml rename {test => util}/src/main/java/org/linguafranca/util/HexViewer.java (79%) rename {database => util}/src/main/java/org/linguafranca/util/MavenRunListener.java (100%) create mode 100644 util/src/main/java/org/linguafranca/util/TestUtil.java diff --git a/database/pom.xml b/database/pom.xml index 8832b7b5..e501874f 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -14,8 +14,9 @@ - org.jetbrains - annotations + org.linguafranca.pwdb + util + ${project.version} com.google.guava @@ -25,5 +26,11 @@ org.bouncycastle bcpkix-jdk18on + + + junit + junit + provided + \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/util/PropertyValueUtil.java b/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java similarity index 94% rename from database/src/main/java/org/linguafranca/util/PropertyValueUtil.java rename to database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java index 7eb17456..0e9b269d 100644 --- a/database/src/main/java/org/linguafranca/util/PropertyValueUtil.java +++ b/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java @@ -1,4 +1,4 @@ -package org.linguafranca.util; +package org.linguafranca.pwdb.util; import org.linguafranca.pwdb.PropertyValue; @@ -29,7 +29,7 @@ public static void genericTest(PropertyValue.Factory factory, boolean testStr // test input byte[] - testValue = factory.of(THIS_IS_A_SECRET.getBytes()); + testValue = factory.of(THIS_IS_A_SECRET.getBytes(StandardCharsets.UTF_8)); test(testStringReturn, testValue); } diff --git a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java index d8c9cffe..94c61281 100644 --- a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java @@ -1,27 +1,26 @@ package org.linguafranca.pwdb; import org.junit.Test; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.nio.charset.StandardCharsets; import static org.junit.Assert.*; -import static org.linguafranca.util.PropertyValueUtil.genericTest; +import static org.linguafranca.pwdb.util.PropertyValueUtil.genericTest; public class PropertyValueTest { public static final String ANOTHER_SECRET = "password with accents àéç"; - Logger logger = LoggerFactory.getLogger(PropertyValueTest.class); @Test public void bytesTest() { - genericTest(PropertyValue.BytesStore.getFactory());} + genericTest(PropertyValue.BytesStore.getFactory()); + } @Test public void sealedObjectTest() { genericTest(PropertyValue.SealedStore.getFactory()); } + @Test public void stringTest() { genericTest(PropertyValue.StringStore.getFactory()); @@ -32,6 +31,5 @@ public void sealedScriptTest(){ PropertyValue.SealedStore sealed = PropertyValue.SealedStore.getFactory().of(ANOTHER_SECRET); byte[] bytes = sealed.getValueAsBytes(); assertArrayEquals(ANOTHER_SECRET.getBytes(StandardCharsets.UTF_8), bytes); - } } \ No newline at end of file diff --git a/example/pom.xml b/example/pom.xml index 5062e9b1..cdac838e 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -38,16 +38,25 @@ KeePassJava2 ${project.version} - - org.linguafranca.pwdb - test - ${project.version} - io.github.novacrypto SecureString 2022.01.17 + + org.slf4j + slf4j-api + + + ch.qos.logback + logback-classic + + + org.linguafranca.pwdb + test + ${project.version} + test-jar + diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 338080e6..2b77c8c0 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -16,7 +16,6 @@ package org.linguafranca.pwdb.kdbx; -import org.junit.BeforeClass; import org.linguafranca.pwdb.*; import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.format.KdbxHeader; @@ -27,11 +26,8 @@ import org.linguafranca.pwdb.security.Encryption; import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; -import static org.junit.Assert.assertEquals; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** @@ -42,17 +38,13 @@ @SuppressWarnings("WeakerAccess") public abstract class QuickStart { + public static final String TEST_OUTPUT_DIR = "testOutput"; static PrintStream printStream = getTestPrintStream(); public abstract Database getDatabase(); public abstract Database loadDatabase(Credentials creds, InputStream inputStream); - @BeforeClass - public static void ensureOutputDir() throws IOException { - Files.createDirectories(Paths.get("testOutput")); - } - /** * Load KDBX */ @@ -90,7 +82,7 @@ public void saveKdbx() throws IOException { } // save to a file with password "123" - try (FileOutputStream outputStream = new FileOutputStream("testOutput/test.kdbx")) { + try (FileOutputStream outputStream = new FileOutputStream(TEST_OUTPUT_DIR + "/test.kdbx")) { database.save(new KdbxCreds("123".getBytes()), outputStream); } } @@ -138,7 +130,7 @@ public void loadKdb() throws IOException { // deep copy from group (not including source group, KDB database has simulated root) kdbxDatabase.getRootGroup().copy(database.getRootGroup()); // save it - try (FileOutputStream f = new FileOutputStream("testOutput/migration.kdbx")) { + try (FileOutputStream f = new FileOutputStream(TEST_OUTPUT_DIR + "/migration.kdbx")) { kdbxDatabase.save(new KdbxCreds("123".getBytes()), f); } } diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java index f6639086..941768b0 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java @@ -53,6 +53,7 @@ public static void listDatabase(String resourceName, Credentials creds, OutputSt public static void listDatabase(DatabaseLoader loader, String resourceName, Credentials creds, OutputStream outputStream) throws IOException { Database database = loader.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); database.save(new StreamFormat.None(), new KdbxCreds.None(), outputStream); + outputStream.flush(); } diff --git a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java index 0f1e2c5d..1448f9c7 100644 --- a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java @@ -2,8 +2,7 @@ import org.junit.Test; -import static org.junit.Assert.*; -import static org.linguafranca.util.PropertyValueUtil.genericTest; +import static org.linguafranca.pwdb.util.PropertyValueUtil.genericTest; public class SecureCharBufferPropertyValueTest { diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index be4001de..aceedbf7 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -16,6 +16,7 @@ package org.linguafranca.pwdb.kdbx; +import org.junit.BeforeClass; import org.junit.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.format.KdbxCreds; @@ -53,6 +54,11 @@ public KdbxDatabase loadDatabase(Credentials credentials, InputStream inputStrea } } + @BeforeClass + public static void ensureOutputDir() throws IOException { + Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); + } + @Test public void saveTest() throws IOException { super.saveKdbx(); diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java index f7a74d03..4a28b2fd 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java @@ -20,10 +20,16 @@ public class UtilTest { @Test public void listXmlTest() throws IOException { PrintWriter writer = new PrintWriter(outputStream); - listXml("V3-CustomIcon.kdbx", new KdbxCreds("123".getBytes()), writer); + listXml("Database-4.1-123.kdbx", new KdbxCreds("123".getBytes()), writer); writer.flush(); } + @Test + public void listXmlTest2() throws IOException { + PrintWriter writer = new PrintWriter(outputStream); + listXml("issue-70/test2.kdbx", new KdbxCreds("KeePassJava2".getBytes()), writer); + writer.flush(); + } /** * List Database Encryption Characteristics */ diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java new file mode 100644 index 00000000..2d2a694a --- /dev/null +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java @@ -0,0 +1,33 @@ +package org.linguafranca.pwdb.kdbx.validation; + +import org.junit.Test; +import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Review Issue-70 https://github.com/jorabin/KeePassJava2/issues/70 + */ +public class Issue70Test { + + public static final String TEST_RESOURCE1 = "issue-70/test2.kdbx"; + public static final KdbxCreds CREDENTIALS1 = new KdbxCreds("KeePassJava2".getBytes()); + public static final String TEST_RESOURCE2 = "Database-4.1-123.kdbx"; + public static final KdbxCreds CREDENTIALS2 = new KdbxCreds("123".getBytes()); + + @Test + public void testCustomData() throws IOException { + try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + KdbxDatabase database = KdbxDatabase.load(CREDENTIALS1, inputStream); + } + } + + @Test + public void testFileFormat_4_1() throws IOException { + try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE2)){ + KdbxDatabase database = KdbxDatabase.load(CREDENTIALS2, inputStream); + } + } +} \ No newline at end of file diff --git a/kdb/pom.xml b/kdb/pom.xml index b11429f2..b979631d 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -18,14 +18,11 @@ database ${project.version} - - org.jetbrains - annotations - org.linguafranca.pwdb test ${project.version} + test-jar test diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index 9be2385d..00a25776 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -44,10 +44,11 @@ 7.0.0 - org.linguafranca.pwdb - test - ${project.version} - test - + org.linguafranca.pwdb + test + ${project.version} + test-jar + test + diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index c135b631..294f7ed0 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -114,7 +114,12 @@ public static KdbxEntry createEntry(KdbxDatabase database) { @JacksonXmlProperty(localName = "OverrideURL") protected String overrideURL; - + + @JacksonXmlProperty(localName = "PreviousParentGroup") + @JsonDeserialize(converter = Base64ToUUIDConverter.class) + @JsonSerialize(converter = UUIDToBase64Converter.class) + protected UUID previousParentGroup; + @JacksonXmlProperty(localName = "Tags") protected String tags; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 2ee79095..5b0c5942 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -102,6 +102,14 @@ public class KdbxGroup extends AbstractGroup{ @JsonSerialize(converter = UUIDToBase64Converter.class) protected UUID lastTopVisibleEntry; + @JacksonXmlProperty(localName = "PreviousParentGroup") + @JsonDeserialize(converter = Base64ToUUIDConverter.class) + @JsonSerialize(converter = UUIDToBase64Converter.class) + protected UUID previousParentGroup; + + @JacksonXmlProperty(localName = "Tags") + protected String tags; + @JacksonXmlProperty(localName = "Entry") /* Workaround jackson */ @JacksonXmlElementWrapper(useWrapping = false) protected List entries; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java index 4f7558b8..8ffdf07e 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java @@ -339,17 +339,30 @@ public String getName() { public static class CustomData { - public CustomData() { + public static class CustomDataItem { + @JacksonXmlProperty(localName = "Key") + public String key; + @JacksonXmlProperty(localName = "Value") + public String value; + @JacksonXmlProperty(localName = "LastModificationTime") + @JsonDeserialize(converter = StringToDateConverter.class) + @JsonSerialize(converter = DateToStringConverter.class) + public Date lastModificationTime; } - public List getAny() { - return any; + @JacksonXmlProperty(localName = "Item") + @JacksonXmlElementWrapper(useWrapping = false) + protected List items; + + public CustomData() { } - public void setAny(List any) { - this.any = any; + public List getItems() { + return items; } - protected List any; + public void setItems(List items) { + this.items = items; + } } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java index 5f9eea33..58050f24 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java @@ -15,8 +15,6 @@ */ package org.linguafranca.pwdb; -import static org.junit.Assert.assertTrue; - import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 2caf71b9..e95b04f6 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -42,19 +42,11 @@ commons-codec commons-codec - - org.jetbrains - annotations - org.linguafranca.pwdb test ${project.version} - test - - - junit - junit + test-jar test diff --git a/pom.xml b/pom.xml index d9a21e5e..aff20c01 100644 --- a/pom.xml +++ b/pom.xml @@ -24,6 +24,7 @@ database test + util kdb kdbx-io kdbx-database @@ -191,17 +192,25 @@ + + org.jetbrains + annotations + provided + junit junit + test org.slf4j slf4j-api + test ch.qos.logback logback-classic + test @@ -244,7 +253,7 @@ junit junit 4.13.2 - + test diff --git a/test/pom.xml b/test/pom.xml index 3ebff937..a86780c2 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -11,6 +11,10 @@ PWDB :: Test Contains the shared test classes for exercising the functionality of a PWDB database implementation. + + true + + org.linguafranca.pwdb @@ -19,4 +23,19 @@ + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + \ No newline at end of file diff --git a/test/src/main/java/org/linguafranca/util/TestUtil.java b/test/src/main/java/org/linguafranca/util/TestUtil.java deleted file mode 100644 index c6433e42..00000000 --- a/test/src/main/java/org/linguafranca/util/TestUtil.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.linguafranca.util; - -import com.google.common.io.ByteStreams; - -import java.io.PrintStream; - -public class TestUtil { - /** - * set system property to suppress output from tests - * @return if "nullOutput has been set, e.g. in a profile - */ - public static PrintStream getTestPrintStream() { - return Boolean.getBoolean("inhibitConsoleOutput") ? - new PrintStream(ByteStreams.nullOutputStream()) : - new PrintStream(System.out); - } -} diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java similarity index 95% rename from test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java rename to test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java index 6e2de18a..eac71040 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java @@ -1,4 +1,4 @@ -/* +package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,9 +14,6 @@ * limitations under the License. */ -package org.linguafranca.pwdb.checks; - -import org.junit.Assert; import org.junit.Test; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; @@ -62,7 +59,7 @@ public void testAddGroup() { Group g2 = database.newGroup(); assertEquals("", g2.getName()); - Assert.assertNotNull(g2.getUuid()); + assertNotNull(g2.getUuid()); assertEquals(0, g2.getIcon().getIndex()); assertEquals(0, g2.getGroups().size()); assertEquals(0, g2.getEntries().size()); @@ -126,9 +123,9 @@ public void testSetFields () { assertEquals("https://window.com", e1.getUrl()); - Assert.assertTrue(e1.match("2")); - Assert.assertTrue(e1.matchTitle("1")); - Assert.assertFalse(e1.matchTitle("doggy")); + assertTrue(e1.match("2")); + assertTrue(e1.matchTitle("1")); + assertFalse(e1.matchTitle("doggy")); Icon ic1 = database.newIcon(27); e1.setIcon(ic1); @@ -153,7 +150,7 @@ public void testSetFields () { properties.add("silly"); // remove all properties to show that getProperties returns all the values we want properties.removeAll(e1.getPropertyNames()); - Assert.assertEquals(0, properties.size()); + assertEquals(0, properties.size()); } catch (UnsupportedOperationException e) { // databases don't have to support arbitrary properties assertFalse(database.supportsNonStandardPropertyNames()); @@ -220,17 +217,17 @@ public void checkAddChangeRemoveProperty() { @Test public void testNewEntry() { Entry e2 = database.newEntry(); - Assert.assertNull(e2.getParent()); + assertNull(e2.getParent()); assertEquals("", e2.getPassword()); - Assert.assertNotNull(e2.getUuid()); + assertNotNull(e2.getUuid()); assertEquals("", e2.getUrl()); assertEquals("", e2.getNotes()); assertEquals("", e2.getUsername()); assertEquals("", e2.getTitle()); - Assert.assertNull(e2.getProperty("silly")); + assertNull(e2.getProperty("silly")); List l = e2.getPropertyNames(); l.removeAll(Entry.STANDARD_PROPERTY_NAMES); - Assert.assertEquals(0, l.size()); + assertEquals(0, l.size()); } @Test diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java similarity index 96% rename from test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java rename to test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java index 886dded5..531a4772 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java @@ -1,4 +1,4 @@ -/* +package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,10 +14,7 @@ * limitations under the License. */ -package org.linguafranca.pwdb.checks; - import com.google.common.io.ByteStreams; -import org.junit.Assert; import org.junit.Test; import org.linguafranca.pwdb.*; @@ -68,7 +65,7 @@ public void getBinaryProperty() throws Exception { InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); assert testFile != null; byte [] original = ByteStreams.toByteArray(testFile); - Assert.assertArrayEquals(original, letterJ); + assertArrayEquals(original, letterJ); } /** @@ -81,7 +78,7 @@ public void getAnotherBinaryProperty() throws Exception { InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); assert testFile != null; byte [] original = ByteStreams.toByteArray(testFile); - Assert.assertArrayEquals(original, letterL); + assertArrayEquals(original, letterL); } /** @@ -95,9 +92,9 @@ public void setBinaryProperty() throws Exception { Entry entry = database.findEntries("Test attachment").get(0); entry.setBinaryProperty("letter L.jpeg", original); byte [] letterL = entry.getBinaryProperty("letter L.jpeg"); - Assert.assertArrayEquals(original, letterL); + assertArrayEquals(original, letterL); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); - Assert.assertTrue(database.isDirty()); + assertTrue(database.isDirty()); } /** diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java similarity index 91% rename from test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java rename to test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java index f63f4448..a870ec18 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java @@ -1,4 +1,4 @@ -/* +package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,8 +14,6 @@ * limitations under the License. */ -package org.linguafranca.pwdb.checks; - import org.junit.Assert; import org.junit.Test; import org.linguafranca.pwdb.*; @@ -58,7 +56,7 @@ public void test123File() throws ParseException { for (Entry tes: tests) { printStream.println(tes.getTitle()); } - Assert.assertEquals(4, tests.size()); + assertEquals(4, tests.size()); if (tests.size() > 0) { // copy the password of the first entry to the clipboard String pass = tests.get(0).getPassword(); @@ -69,12 +67,12 @@ public void test123File() throws ParseException { */ // all the relevant entries should have the password 123 String pass2 = tests.get(0).getPassword(); - Assert.assertEquals(pass, pass2); - Assert.assertEquals("123", pass2); + assertEquals(pass, pass2); + assertEquals("123", pass2); } List passwords = database.findEntries("password"); - Assert.assertEquals(4, passwords.size()); + assertEquals(4, passwords.size()); for (Entry passwordEntry : passwords) { assertEquals(passwordEntry.getTitle(), passwordEntry.getPassword()); printStream.println(passwordEntry.getTitle()); @@ -86,7 +84,7 @@ public boolean matches(Entry entry) { return entry.getTitle().equals("hello world"); }}); - Assert.assertEquals(1, entries.size()); + assertEquals(1, entries.size()); assertEquals("pass", entries.get(0).getPassword()); // kdb files don't have a time zone so can't make head or tail of the date - test file seems to have a local time in it @@ -96,6 +94,6 @@ public boolean matches(Entry entry) { Date c = entries.get(0).getCreationTime(); Date expected = sdf.parse("2015-10-24T17:20:41Z"); - Assert.assertEquals(expected, c); + assertEquals(expected, c); } } diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java similarity index 99% rename from test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java rename to test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java index d82f5329..95343a5b 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java @@ -1,4 +1,4 @@ -/* +package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,8 +14,6 @@ * limitations under the License. */ -package org.linguafranca.pwdb.checks; - import org.junit.BeforeClass; import org.junit.Test; import org.linguafranca.pwdb.*; diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java similarity index 98% rename from test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java rename to test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java index 21f06bce..691c3877 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java @@ -4,7 +4,6 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.Icon; import static org.junit.Assert.*; diff --git a/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java similarity index 95% rename from test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java rename to test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java index 5a428ff3..1a330df6 100644 --- a/test/src/main/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java @@ -1,4 +1,4 @@ -/* +package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,9 +14,6 @@ * limitations under the License. */ -package org.linguafranca.pwdb.checks; - -import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -66,7 +63,7 @@ public void saveAndReloadTest() throws IOException { FileOutputStream fos = new FileOutputStream("testOutput/test1.kdbx"); saveDatabase(output, getCreds("123".getBytes()), fos); - Assert.assertFalse(output.isDirty()); + assertFalse(output.isDirty()); fos.flush(); fos.close(); // make sure that saving didn't mess up content @@ -207,13 +204,13 @@ public void inspectNewDatabase () throws IOException { public void testNewDatabase() throws IOException { Database database = getDatabase(); Group root = database.getRootGroup(); - Assert.assertTrue(root.isRootGroup()); + assertTrue(root.isRootGroup()); assertEquals(0, root.getGroups().size()); assertEquals(0, root.getEntries().size()); - Assert.assertTrue(database.shouldProtect("Password")); - Assert.assertFalse(database.shouldProtect("Title")); - Assert.assertFalse(database.shouldProtect("Bogus")); + assertTrue(database.shouldProtect("Password")); + assertFalse(database.shouldProtect("Title")); + assertFalse(database.shouldProtect("Bogus")); assertEquals("New Database", database.getName()); database.setName("Modified Database"); @@ -228,8 +225,8 @@ public void testNewDatabase() throws IOException { root.addGroup(group1); assertEquals("Group 1", group1.getName()); - Assert.assertFalse(group1.isRootGroup()); - Assert.assertTrue(root.isRootGroup()); + assertFalse(group1.isRootGroup()); + assertTrue(root.isRootGroup()); assertEquals(1, root.getGroups().size()); assertEquals(newGroupUUID, root.getGroups().get(0).getUuid()); @@ -246,7 +243,7 @@ public void testNewDatabase() throws IOException { try { root.setParent(group1); - Assert.fail("Cannot add root group to another group"); + fail("Cannot add root group to another group"); } catch (Exception ignored) { } diff --git a/test/src/main/resources/Attachment.kdbx b/test/src/test/resources/Attachment.kdbx similarity index 100% rename from test/src/main/resources/Attachment.kdbx rename to test/src/test/resources/Attachment.kdbx diff --git a/test/src/main/resources/Attachment.xml b/test/src/test/resources/Attachment.xml similarity index 99% rename from test/src/main/resources/Attachment.xml rename to test/src/test/resources/Attachment.xml index e400d526..5bdd5620 100755 --- a/test/src/main/resources/Attachment.xml +++ b/test/src/test/resources/Attachment.xml @@ -1,157 +1,157 @@ - - - - KeePass - - 2016-08-27T14:41:45Z - - 2016-08-27T14:41:45Z - - 2016-08-27T14:41:45Z - 365 - - 2016-08-27T14:41:58Z - -1 - -1 - - False - False - True - False - False - - True - AAAAAAAAAAAAAAAAAAAAAA== - 2016-08-27T14:41:45Z - AAAAAAAAAAAAAAAAAAAAAA== - 2016-08-27T14:41:45Z - 10 - 6291456 - AAAAAAAAAAAAAAAAAAAAAA== - AAAAAAAAAAAAAAAAAAAAAA== - - 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 - - - - - - TOEWAvT9GlIjOkSMv9F+aw== - Root - - 48 - - 2016-08-27T14:41:45Z - 2016-08-27T14:41:45Z - 2016-08-27T14:41:45Z - 2016-08-27T14:41:45Z - False - 0 - 2016-08-27T14:41:45Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - GPv+n0B4upj0sZd+nuuiBQ== - 0 - - - - - - 2016-08-27T14:42:09Z - 2016-08-27T14:43:51Z - 2016-09-11T11:11:59Z - 2016-08-27T14:42:09Z - False - 1 - 2016-08-27T14:43:51Z - - - Notes - - - - Password - - - - Title - Test attachment - - - URL - - - - UserName - - - - letter J.jpeg - - - - True - 0 - - - - - mwq3nFNpPrvjXlsWy/6FTQ== - 0 - - - - - - 2016-08-27T15:03:52Z - 2016-08-27T15:04:44Z - 2016-08-27T15:04:44Z - 2016-08-27T15:03:52Z - False - 0 - 2016-08-27T15:04:44Z - - - Notes - - - - Password - - - - Title - Test 2 Attachments - - - URL - - - - UserName - - - - letter J.jpeg - - - - letter L.jpeg - - - - True - 0 - - - - - - + + + + KeePass + + 2016-08-27T14:41:45Z + + 2016-08-27T14:41:45Z + + 2016-08-27T14:41:45Z + 365 + + 2016-08-27T14:41:58Z + -1 + -1 + + False + False + True + False + False + + True + AAAAAAAAAAAAAAAAAAAAAA== + 2016-08-27T14:41:45Z + AAAAAAAAAAAAAAAAAAAAAA== + 2016-08-27T14:41:45Z + 10 + 6291456 + AAAAAAAAAAAAAAAAAAAAAA== + AAAAAAAAAAAAAAAAAAAAAA== + + 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 + + + + + + TOEWAvT9GlIjOkSMv9F+aw== + Root + + 48 + + 2016-08-27T14:41:45Z + 2016-08-27T14:41:45Z + 2016-08-27T14:41:45Z + 2016-08-27T14:41:45Z + False + 0 + 2016-08-27T14:41:45Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + GPv+n0B4upj0sZd+nuuiBQ== + 0 + + + + + + 2016-08-27T14:42:09Z + 2016-08-27T14:43:51Z + 2016-09-11T11:11:59Z + 2016-08-27T14:42:09Z + False + 1 + 2016-08-27T14:43:51Z + + + Notes + + + + Password + + + + Title + Test attachment + + + URL + + + + UserName + + + + letter J.jpeg + + + + True + 0 + + + + + mwq3nFNpPrvjXlsWy/6FTQ== + 0 + + + + + + 2016-08-27T15:03:52Z + 2016-08-27T15:04:44Z + 2016-08-27T15:04:44Z + 2016-08-27T15:03:52Z + False + 0 + 2016-08-27T15:04:44Z + + + Notes + + + + Password + + + + Title + Test 2 Attachments + + + URL + + + + UserName + + + + letter J.jpeg + + + + letter L.jpeg + + + + True + 0 + + + + + + \ No newline at end of file diff --git a/test/src/test/resources/Database-4.1-123.kdbx b/test/src/test/resources/Database-4.1-123.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..024017e1559c798666c0119016ba62f879599483 GIT binary patch literal 2759 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 N(V?M!1wjA+004bUJc$4R literal 0 HcmV?d00001 diff --git a/test/src/main/resources/EmptyPassword.kdbx b/test/src/test/resources/EmptyPassword.kdbx similarity index 100% rename from test/src/main/resources/EmptyPassword.kdbx rename to test/src/test/resources/EmptyPassword.kdbx diff --git a/test/src/main/resources/EmptyPasswordWithKey.kdbx b/test/src/test/resources/EmptyPasswordWithKey.kdbx similarity index 100% rename from test/src/main/resources/EmptyPasswordWithKey.kdbx rename to test/src/test/resources/EmptyPasswordWithKey.kdbx diff --git a/test/src/main/resources/EmptyPasswordWithKey.key b/test/src/test/resources/EmptyPasswordWithKey.key similarity index 100% rename from test/src/main/resources/EmptyPasswordWithKey.key rename to test/src/test/resources/EmptyPasswordWithKey.key diff --git a/test/src/main/resources/ExampleDatabase.kdbx b/test/src/test/resources/ExampleDatabase.kdbx similarity index 100% rename from test/src/main/resources/ExampleDatabase.kdbx rename to test/src/test/resources/ExampleDatabase.kdbx diff --git a/test/src/main/resources/ExampleDatabase.xml b/test/src/test/resources/ExampleDatabase.xml similarity index 99% rename from test/src/main/resources/ExampleDatabase.xml rename to test/src/test/resources/ExampleDatabase.xml index c7367899..6535b834 100755 --- a/test/src/main/resources/ExampleDatabase.xml +++ b/test/src/test/resources/ExampleDatabase.xml @@ -1,583 +1,583 @@ - - - - KeePass - - 2016-09-10T16:29:17Z - - 2016-09-10T16:29:17Z - - 2016-09-10T16:29:17Z - 365 - #FF0000 - 2016-09-10T16:29:17Z - -1 - -1 - - False - False - True - False - False - - - - kJh5KNGNaU6SZIsfsAwx7g== - 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 - - - True - 4fkGUxKnlUae43TI8NsnLQ== - 2016-09-10T16:29:17Z - V/rJOkFjYUqrFCDdP8HeSw== - 2016-09-10T16:36:55Z - 10 - 6291456 - SkjqCvDUmU6y6k7onr6ewg== - oy863aK0cEKApjbJfGLHZQ== - - H4sIAAAAAAAEAJWXZ1AT3rbFEwIEQldAgRgUIr0IUkOLhCqG3omIAlJDb6EIf1CB0KRLE5TQe0cUpAgoAZUaCb0I0kR6l4f3znvz7se79/q2ZvZvzTkz58w+/34+C2C+q6GtAQACAQDgRQPOJwBPAbTUYBowNS0NmAYCoaVjYGdioKdn4GJlY2a/fo2H5/o1bm64gJQInE+Cn5tbVElUQloWgUDwCquoqcihpOQRskAIBMJAz8DJxMQpe5P7pux/XeftABYaAA6AAwFZABQsQBAL8PwjAHqRkQr4N+r/FYiSihpMAaS5cDWYAUAQCAiioaIBg6n+mkAKECWAioX60g0J8GWeOwYPPf9hS3xd08EqqWr4qHbwN69X0pvOua2bUkbePuHJ7HAZNeOLOVwXAArQf1AoqShA1EDwhSl9EQcIoqAAg0GUoH+bfyksVDcuSfBI/kUMUl9mVf3ndQ3v7URDo445NrjUORlAD7qYyQJiAagAJh0pwgQowvCtq8Lo6NZ3hKWXcHvR5NSyDl7cnj6bg/TpCb5Pajo79YaSXp8iWe8JtV4VK5jWTGC4xeXJOBAcbmULZ2K7jPT9GeLnfw4YpQFXa7hz+4VtYbdj6O1Lxlaaj8U1e0Lt8ADO+SNXfc5ItEK1rOChDhmmwL+VJ9IoamHXNsd+9El8gU8h7HXHn+EPo0j0NyqA/r9F27ZjMG71tTmNrHo3rQFK+c8YGojMKRMjZ6rO/lwiGtwZvz/wcescIMKNy+MI5EevhJekv6We8zDom8RZ2b3aduHQVrfPcjIoscTcsrAzIaaQflvUdaFE49oTE5Z/FW+36JTGZCKg03v2CZRmhNUB9VtJJX1IJVQZKfPbr8cnOohA0dpl6FFNVcGmQyjh/v4TtoQPuTjeP3wTx1cmrZWLRlgYh/ibK3/YzuH3MGi/zFLHsaH7GJzkk6k+bEc5zzKHeFPsJdlcg1MXH7fcfjyZfAUrGMJdIskxFNihuMB1aylurudL54ugoNJ7QhrBeMv9Buy0kMgTsMFmZoSmXJr4nL+OHmU/kjjAWH72cGOr/NOP4xll0T4u7+ttkQstkpHMXgyApb/3J0A5g2ter1hOS5vA3tSxjKdAzqOBmZ8S+VM4Ppa4xBXalrMSwpPhETup+wE2T5D0vhq1CY62g5YhtFrIcSE9s2WSpdCY/ri3qKvUZ4zPcn0oX2BVKgYm1oLxdVjxe8JGbZjyymJkMF83Vs6mOjKqsaI+38WOTQBwO7mt2inJVk5MLdymxxGrWOtJ9hCeZn37Mmg1aTWxaj09feCXRqAyWzSffM9Cs2tvSKzIXP8elHMPPmDCL0d0Q+Fe0CFZd/+Ru7qgUY3jEEw8Ebu1inEsaWL2SmKkqTJyJBkmZXZZ3Gt1EvW6q954yryg87Ogm2EaXDc1Z+SEcXCCjv5J7Bh+TXw3/C0Dl4GQfPje3I9gMaNgdIPhbEJrXf8wsICVL9KlWaOl47HPFVTNtJYhqzJv8YE+40+KsNtIB0uyd0v8cZywu0dUxUNiTLRiAPPMxyN9ZvKMfoNjVIxokT2VYCc+MHJ5wlLYZNYcQ0RLds3z9yqXnhxyEJ3EhIUNPsvDN/nKxkmdcBKBmazK4TrrNtPSOq9ozBUUpSBIMk7ShQS4jjEsJnN64uUMMfoT5vzvSc8tMPG8vYmIoFvzJQJW0UKfdQNstyp2BUsTvKqM20qcrvbRZTg6JJe1/DaTsFUxoeWGpFTY6Ovcbwnp2vr88QfBm0RLyTCLtYWVfp1Exu+5VOIEVwQc33Fa4icfjtabti7OqXkWS98iDA6P9/kKaOCuorHAbm+k6xhl25U4YhH/cfYzOqUBzIKuj5XhwfJ67lffe01l23WZo+Ws3taZOw8Wc+u0TWNGeabnzwE1YmVEtVcxTLCrv8xAM/HHafqcNnkQCmHj+4MPq/vfilBYuO86qWg7eGlm8IR9FckYbrFUJzv3FaFvktgDNoThmVv1aRY5Ql5l0jQ7j50ED272eR7oaXdl+/mbYAiLhxlT+vmct4znPjPO907o7vxjvgBPO/SD7qClkt7IRG0UsuBTsq7YUMpQUwdkbnHo+PFK9K8TAkix49q2nliZNhejiXSyca7noXGW5pC9eD3dLs8peis/bSiotH3hq0krX1i7u+dQHMpfYzel/0ETeMGkgYTOKUyW/V3D+8oVbgMdSxpUjGOrJ3e+uGvMqBrJ7DIycBs/pTyMfohdu8beiOlIGtKSWzXnTl429SnqhWar7zV1kTzW17fFMLnWpKBh17c3LTI4NBRzSXoPXnbsVDxVHUA6ZJcolySvm8JhMV8zmOC+BVxhyg7KxlRWKW8nKKQb8p8WXE+cS/ixWkyS7DQQ272raGJvsh+aFNvnVRr+pZDOC/0qxsZ3EEZ8KVcepoCWDGL1g1ZRsPgG6XJ2Zy3EXpsgDUxjK0GdSFi1S0jdmWqRVTyWSzk8qqJ4ILdWqH+eKgECgoxpibgI1+oNW0JhjqA5kwMl76pBGLZm8pMDo7qm2CPpAIOrA+zxaDdq6blPeJX8F7ELNaijy6slpkMfq/VdjAOKL57G0Nx9jI/Vq2jSgY4DNX8tN1bJymlhDTxuFG8z5Ejf+TtfPFUTH2/EeBlL8Lucyt26nicQSL6TWPzg82RcF+lMiKprJcc6jpHL9aBRrVr2cb7kL2hkkG3jc3N5oRWQtd2e2oFbhqS+nMIXF2iWMRD8yFh9mUuSS+W9sC/4OKpitGTpi8O3xgbsHbuzzAY5lilgtFd8TpyT+Eb1UNvvQ2O6ylrEAHApNj3aiu9N+vfLPm5sK7HPXMVfkprFQLGKKbkUnlsV9dN8GQ+CGBVwvR1nTVIuSbFYXGERfMFpu69mjD0pm5tcdjtSGgESNazpu3/6Ku+Lonz9N46lgpmQjNxKpc6ysj8LCtfEzwFw5Q9p0rZLR6bPrVAvKh5htQ44qhjw/M0Au1+gyhqZh6Lf6VleeM+hpJKnoArN9UEw903IBOqABWSScCZ/xK1qwYiN5a4Vmj1qPg7rxumeAwbunwO0SNP3ke/WeCv2fR+nnPZ1q4mT5fyXEjg/dap6fPjQ03v46aSzLkEKVXXivR0aoTWCV04fJrgrJzlsFlbo0ozVIO3NurZ1jfngscGFXLoRE90qXUgDX2/1soWPv80/lZ6kRuc8TlAWxvjLaPQqQf2NH1sbWdHF7cVrS0TPszbdX3pUXzQ199QDxtdolI/IjvIPzGl9s6LYNj9ls/jHpMv+pGhgPu1P1KlzZ857G68rLa1BlfGesPnWwAaVoiIFRsMr/QVqtT+1BQ2Dttrj2Rhb36fUsfoE6lZoGLMKERlorPLf+uTCFE3H6iMNi67UIgFQUuMuf3cGL4ODcd7GFMKL6PBNpkeH2dJ1i3bGVPMSIk9CzOCazvHY48/cb+wLE+L3QplL13qHtNbPsgrPPhbMm9EUR6dvR2W9F5bSzFSolbSzfi2DmNHeBqtApqgy4GbWeuHTXr/t/zgdWTcMe/TsbFd6X/tE9aDL1IHeJehEgH4i1ZReoTt++4WfzCjbkAKTtvzZKkVWJS925zazbPAobOEMXPlmStOe5wBVgU8inPj/gRChziAEWctQX7GDF04BCQ9IHKMSrC9sTnB3Gkl9n8r/3NWSp8NJvSHawJDcOcRKsy7tUROXkIUeIBb1NxfcANECgFPWL+P4rXi+6MKlUgPwwKtpcixX6omC4M6wpDmkbPO9xzO0R0lpzETR1ArNYqC1bMT3Iv6nwSd8Rsk/Pr86vPFZt2SIiTWiHQegMhrLhU4opxZOBEuX2xztR2eO3Uviau2si2Z/0yH/+tFOuqx9sQ9vU5jemBnTY2f7em+R7Okmq/DwwjaHffUulKRbdt7drNgGt7jqVF9slX+zUBG0plSfOXCMuHImLUOBXE6Ja6Rd9VQY3ri6kycZwBjTHX1PS1tnIB778ZWg/qRm3bh5n5AO/YRqffFInNaOusm9UoU+J1tCRITJZV1x+mPp8jlTCEFewu53hdqi+a5uqXVW6OLQfqqLpwWE42Z4wDmgacpo4xe7nJ2X5ZfY7bVv1o7f59AJCAQ3fvsa1lbQkO2Fup68RmaAbQIERgVIKRFRi397KQd1L89ex9Mc4J8JI0x0Spx5l30heKg2bfCNPKMStGO2S+zyZ06c6cnDYIJrEFNcGknOrfaL+G4kYrgL1SPjwwaFX9JuB1VeHFPp9+ZaruGW4HQPmOAy9OyRHM6k4aqHJvirL1dVdOXiW9H0xDwvW3uv0cIXp4SU/ld1N2cDYk8HT67jkq20braFCdCnP7A0K9wDF87nn8jMvtemnR2eaoMcHH5jW5uk7EMmX7W3cQetQKK9UXGemhmhb5ydyU+Wyn6q1O3aUTAKVf/6VsCkXjFcZJmzLOoNw+tupwgLmOEZNxFTkJ1IntpNrYa30wZdm+pJsgItcSnrLddVpgtHItalb1i3jRh9HY9azteHe8UVT121Q9rWP0+p+5GMVyB9f6r0Er82csJGTDk68McR65d/vPBKaS6/oUzD9X5Vce1LMDHmPt/YRECveGuUaVL/dtgPzWL9y5aB5EvqPVqdRq1Py1a4y4NKfSugu4tegfuENzkN/B/6T94raeeouJpWj0RjbUqHgZjFiJHVnbcflPnf9ZQzIseN2t26gRsAfYbKW7aYvA8QlzvfN6+/dlR6FNbjNvY5ExH6qTAd1tgSumH/CrxeVoOaGJ2kDOmczEDl4pEem72GH3JQVa5FQwm384d8e91ShU66avarjFECggUpcqk3UcWxJkExMV2aLrrlQt26TAK7zC6Xb5MBeRBAntwXLVrK5HbGll2ZJCIfdt8nn2nyOWA67Xbv3jtFISyd6bGRPNroAV43tYy4kV5UfJSiN9Ik4VzHsHzyMdaHN7/+JGztwBwaqjRe7bQmokNVkhXM+mLCllC3k21mmTrtSCv0LwwnZmNQrDr7mUd85ILcLYKIXcAB7BjEN4KQ2Sw0fcte/aNvRPIugNgfv7UbGbDn+40ZjX7m7Q4ZN1XQZVVcw6youlxSK7Mi79cOCNpundK8c6Jcur2wh5naKPauDeVFuLg7LcRiUKut3MVTpkfFsx3mG3cgFZeaymCd82l1Dw2wBuJK8co6F19P6jngcODhr9JQHXhwSNGB7Wl+vdVyc6BEmgJp8GDs7jOz7cxS57FbyyYhsqeMlnVlsSUMjcqlCOdTYR8RwvFXKnRv/QhPjYaYXq/92iE0g/1Ex76nU/91d3d2FbG20Og1JDhxwNo5XTbIOG2KaP/nD+FZjO70nvNQPdXEhvPRUI42nUfI073D31sV3biK7yIeIsyXGcnt2JrWuwoj8221CRZ58OJ/LZEXkkCaSsOe1jwYK8qNLd7++qffqPqDcgEOT5GskHwfRatGrjQoklJ2hlOQ27emmWrDngUOlM4VRyP8DEArtwxnVWrRgOcDP6FcDDMqK5hTAaqsRudoNRqylyf/qT4X93t1oDhdcquBYoSysYK3AwftmdCprE2QI11cRHIZ2+sRbVyT2KUhL1iaaqOeVwBTCNC9/ZoxuB6IZP3/AoeHCTA5/m9iAabz8f8Bl7U9dHoQAAA= - - - - - - oy863aK0cEKApjbJfGLHZQ== - ExampleDatabase - - 49 - - 2016-09-10T16:29:17Z - 2016-09-10T16:29:17Z - 2016-09-10T18:50:45Z - 2016-09-10T15:04:19Z - False - 19 - 2016-09-10T16:29:17Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - 9truGTw7i0+svm1EKjSCXQ== - 0 - - - - - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T15:04:19Z - False - 0 - 2016-09-10T16:29:32Z - - - Notes - Notes - - - Password - Password - - - Title - Sample Entry - - - URL - http://keepass.info/ - - - UserName - User Name - - - True - 0 - - Target Window - {USERNAME}{TAB}{PASSWORD}{TAB}{ENTER} - - - - - - qgRU1hFDE0az+mXL5mTrFg== - 0 - - - - - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T15:04:19Z - False - 0 - 2016-09-10T16:29:32Z - - - Password - 12345 - - - Title - Sample Entry #2 - - - URL - http://keepass.info/help/kb/testform.html - - - UserName - Michael321 - - - True - 0 - - *Test Form - KeePass* - - - - - - - y5JBCSQMd0qKhGu3M9XjIw== - General - - 48 - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T18:50:41Z - 2016-09-10T15:04:19Z - False - 2 - 2016-09-10T16:29:32Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - - 0w2ac9Zj6Uqp6Up46CyNhQ== - Windows - - 38 - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T18:50:36Z - 2016-09-10T15:04:19Z - False - 2 - 2016-09-10T16:29:32Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - - zlA7tzCMp0WLJD8Bjw+rCw== - Network - - 3 - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T18:50:36Z - 2016-09-10T15:04:19Z - False - 1 - 2016-09-10T16:29:32Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - - JATpnerLXU+0DtRco5cx6Q== - Internet - - 1 - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T18:50:37Z - 2016-09-10T15:04:19Z - False - 2 - 2016-09-10T16:29:32Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - - KS6v+Pw92EmIby9paykzpQ== - eMail - - 19 - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T18:50:38Z - 2016-09-10T15:04:19Z - False - 1 - 2016-09-10T16:29:32Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - - 4fkGUxKnlUae43TI8NsnLQ== - Recycle Bin - - 43 - - 2016-09-10T16:30:02Z - 2016-09-10T16:30:02Z - 2016-09-10T18:50:45Z - 2016-09-10T15:04:19Z - False - 7 - 2016-09-10T16:30:02Z - - True - - false - false - AAAAAAAAAAAAAAAAAAAAAA== - - SkjqCvDUmU6y6k7onr6ewg== - Homebanking - - 37 - - 2016-09-10T16:29:32Z - 2016-09-10T16:29:32Z - 2016-09-10T18:50:45Z - 2016-09-10T15:04:19Z - False - 8 - 2016-09-10T16:30:02Z - - True - - null - null - qYwf0cP0sU6mfMsfj+s9EA== - - qYwf0cP0sU6mfMsfj+s9EA== - 37 - kJh5KNGNaU6SZIsfsAwx7g== - - - - c;b;a - - 2016-09-10T16:29:50Z - 2016-09-11T10:44:40Z - 2016-09-11T10:44:40Z - 2016-09-10T15:04:19Z - False - 9 - 2016-09-10T16:29:50Z - - - Notes - - - - Password - 1abEUk2xWlLtfDIvzwYJ - - - Title - BinaryField - - - URL - - - - UserName - - - - letter J.jpeg - - - - True - 0 - - - - qYwf0cP0sU6mfMsfj+s9EA== - 37 - - - - - - 2016-09-10T16:29:50Z - 2016-09-10T16:29:56Z - 2016-09-10T16:29:56Z - 2016-09-10T15:04:19Z - False - 1 - 2016-09-10T16:29:50Z - - - Notes - - - - Password - 1abEUk2xWlLtfDIvzwYJ - - - Title - hhs - - - URL - - - - UserName - - - - True - 0 - - - - qYwf0cP0sU6mfMsfj+s9EA== - 37 - kJh5KNGNaU6SZIsfsAwx7g== - - - - - - 2016-09-10T16:29:50Z - 2016-09-10T17:26:56Z - 2016-09-10T17:45:44Z - 2016-09-10T15:04:19Z - False - 3 - 2016-09-10T16:29:50Z - - - Notes - - - - Password - 1abEUk2xWlLtfDIvzwYJ - - - Title - hhs - - - URL - - - - UserName - - - - True - 0 - - - - qYwf0cP0sU6mfMsfj+s9EA== - 37 - kJh5KNGNaU6SZIsfsAwx7g== - - - - - - 2016-09-10T16:29:50Z - 2016-09-10T18:39:40Z - 2016-09-10T18:40:02Z - 2016-09-10T15:04:19Z - False - 6 - 2016-09-10T16:29:50Z - - - Notes - - - - Password - 1abEUk2xWlLtfDIvzwYJ - - - Title - hhs - - - URL - - - - UserName - - - - letter J.jpeg - - - - True - 0 - - - - qYwf0cP0sU6mfMsfj+s9EA== - 37 - kJh5KNGNaU6SZIsfsAwx7g== - - - - a b c - - 2016-09-10T16:29:50Z - 2016-09-10T18:50:20Z - 2016-09-10T18:50:20Z - 2016-09-10T15:04:19Z - False - 7 - 2016-09-10T16:29:50Z - - - Notes - - - - Password - 1abEUk2xWlLtfDIvzwYJ - - - Title - hhs - - - URL - - - - UserName - - - - letter J.jpeg - - - - True - 0 - - - - qYwf0cP0sU6mfMsfj+s9EA== - 37 - kJh5KNGNaU6SZIsfsAwx7g== - - - - c;b;a - - 2016-09-10T16:29:50Z - 2016-09-10T18:51:05Z - 2016-09-10T18:51:05Z - 2016-09-10T15:04:19Z - False - 8 - 2016-09-10T16:29:50Z - - - Notes - - - - Password - 1abEUk2xWlLtfDIvzwYJ - - - Title - hhs - - - URL - - - - UserName - - - - letter J.jpeg - - - - True - 0 - - - - - - - - V/rJOkFjYUqrFCDdP8HeSw== - Templates - - 48 - - 2016-09-10T16:36:24Z - 2016-09-10T16:36:29Z - 2016-09-10T18:50:40Z - 2016-09-09T23:00:00Z - False - 4 - 2016-09-10T16:36:32Z - - True - - null - null - AAAAAAAAAAAAAAAAAAAAAA== - - - - + + + + KeePass + + 2016-09-10T16:29:17Z + + 2016-09-10T16:29:17Z + + 2016-09-10T16:29:17Z + 365 + #FF0000 + 2016-09-10T16:29:17Z + -1 + -1 + + False + False + True + False + False + + + + kJh5KNGNaU6SZIsfsAwx7g== + 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 + + + True + 4fkGUxKnlUae43TI8NsnLQ== + 2016-09-10T16:29:17Z + V/rJOkFjYUqrFCDdP8HeSw== + 2016-09-10T16:36:55Z + 10 + 6291456 + SkjqCvDUmU6y6k7onr6ewg== + oy863aK0cEKApjbJfGLHZQ== + + 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 + + + + + + oy863aK0cEKApjbJfGLHZQ== + ExampleDatabase + + 49 + + 2016-09-10T16:29:17Z + 2016-09-10T16:29:17Z + 2016-09-10T18:50:45Z + 2016-09-10T15:04:19Z + False + 19 + 2016-09-10T16:29:17Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + 9truGTw7i0+svm1EKjSCXQ== + 0 + + + + + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T15:04:19Z + False + 0 + 2016-09-10T16:29:32Z + + + Notes + Notes + + + Password + Password + + + Title + Sample Entry + + + URL + http://keepass.info/ + + + UserName + User Name + + + True + 0 + + Target Window + {USERNAME}{TAB}{PASSWORD}{TAB}{ENTER} + + + + + + qgRU1hFDE0az+mXL5mTrFg== + 0 + + + + + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T15:04:19Z + False + 0 + 2016-09-10T16:29:32Z + + + Password + 12345 + + + Title + Sample Entry #2 + + + URL + http://keepass.info/help/kb/testform.html + + + UserName + Michael321 + + + True + 0 + + *Test Form - KeePass* + + + + + + + y5JBCSQMd0qKhGu3M9XjIw== + General + + 48 + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T18:50:41Z + 2016-09-10T15:04:19Z + False + 2 + 2016-09-10T16:29:32Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + + 0w2ac9Zj6Uqp6Up46CyNhQ== + Windows + + 38 + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T18:50:36Z + 2016-09-10T15:04:19Z + False + 2 + 2016-09-10T16:29:32Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + + zlA7tzCMp0WLJD8Bjw+rCw== + Network + + 3 + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T18:50:36Z + 2016-09-10T15:04:19Z + False + 1 + 2016-09-10T16:29:32Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + + JATpnerLXU+0DtRco5cx6Q== + Internet + + 1 + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T18:50:37Z + 2016-09-10T15:04:19Z + False + 2 + 2016-09-10T16:29:32Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + + KS6v+Pw92EmIby9paykzpQ== + eMail + + 19 + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T18:50:38Z + 2016-09-10T15:04:19Z + False + 1 + 2016-09-10T16:29:32Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + + 4fkGUxKnlUae43TI8NsnLQ== + Recycle Bin + + 43 + + 2016-09-10T16:30:02Z + 2016-09-10T16:30:02Z + 2016-09-10T18:50:45Z + 2016-09-10T15:04:19Z + False + 7 + 2016-09-10T16:30:02Z + + True + + false + false + AAAAAAAAAAAAAAAAAAAAAA== + + SkjqCvDUmU6y6k7onr6ewg== + Homebanking + + 37 + + 2016-09-10T16:29:32Z + 2016-09-10T16:29:32Z + 2016-09-10T18:50:45Z + 2016-09-10T15:04:19Z + False + 8 + 2016-09-10T16:30:02Z + + True + + null + null + qYwf0cP0sU6mfMsfj+s9EA== + + qYwf0cP0sU6mfMsfj+s9EA== + 37 + kJh5KNGNaU6SZIsfsAwx7g== + + + + c;b;a + + 2016-09-10T16:29:50Z + 2016-09-11T10:44:40Z + 2016-09-11T10:44:40Z + 2016-09-10T15:04:19Z + False + 9 + 2016-09-10T16:29:50Z + + + Notes + + + + Password + 1abEUk2xWlLtfDIvzwYJ + + + Title + BinaryField + + + URL + + + + UserName + + + + letter J.jpeg + + + + True + 0 + + + + qYwf0cP0sU6mfMsfj+s9EA== + 37 + + + + + + 2016-09-10T16:29:50Z + 2016-09-10T16:29:56Z + 2016-09-10T16:29:56Z + 2016-09-10T15:04:19Z + False + 1 + 2016-09-10T16:29:50Z + + + Notes + + + + Password + 1abEUk2xWlLtfDIvzwYJ + + + Title + hhs + + + URL + + + + UserName + + + + True + 0 + + + + qYwf0cP0sU6mfMsfj+s9EA== + 37 + kJh5KNGNaU6SZIsfsAwx7g== + + + + + + 2016-09-10T16:29:50Z + 2016-09-10T17:26:56Z + 2016-09-10T17:45:44Z + 2016-09-10T15:04:19Z + False + 3 + 2016-09-10T16:29:50Z + + + Notes + + + + Password + 1abEUk2xWlLtfDIvzwYJ + + + Title + hhs + + + URL + + + + UserName + + + + True + 0 + + + + qYwf0cP0sU6mfMsfj+s9EA== + 37 + kJh5KNGNaU6SZIsfsAwx7g== + + + + + + 2016-09-10T16:29:50Z + 2016-09-10T18:39:40Z + 2016-09-10T18:40:02Z + 2016-09-10T15:04:19Z + False + 6 + 2016-09-10T16:29:50Z + + + Notes + + + + Password + 1abEUk2xWlLtfDIvzwYJ + + + Title + hhs + + + URL + + + + UserName + + + + letter J.jpeg + + + + True + 0 + + + + qYwf0cP0sU6mfMsfj+s9EA== + 37 + kJh5KNGNaU6SZIsfsAwx7g== + + + + a b c + + 2016-09-10T16:29:50Z + 2016-09-10T18:50:20Z + 2016-09-10T18:50:20Z + 2016-09-10T15:04:19Z + False + 7 + 2016-09-10T16:29:50Z + + + Notes + + + + Password + 1abEUk2xWlLtfDIvzwYJ + + + Title + hhs + + + URL + + + + UserName + + + + letter J.jpeg + + + + True + 0 + + + + qYwf0cP0sU6mfMsfj+s9EA== + 37 + kJh5KNGNaU6SZIsfsAwx7g== + + + + c;b;a + + 2016-09-10T16:29:50Z + 2016-09-10T18:51:05Z + 2016-09-10T18:51:05Z + 2016-09-10T15:04:19Z + False + 8 + 2016-09-10T16:29:50Z + + + Notes + + + + Password + 1abEUk2xWlLtfDIvzwYJ + + + Title + hhs + + + URL + + + + UserName + + + + letter J.jpeg + + + + True + 0 + + + + + + + + V/rJOkFjYUqrFCDdP8HeSw== + Templates + + 48 + + 2016-09-10T16:36:24Z + 2016-09-10T16:36:29Z + 2016-09-10T18:50:40Z + 2016-09-09T23:00:00Z + False + 4 + 2016-09-10T16:36:32Z + + True + + null + null + AAAAAAAAAAAAAAAAAAAAAA== + + + + \ No newline at end of file diff --git a/test/src/main/resources/KeyFileDatabase.kdbx b/test/src/test/resources/KeyFileDatabase.kdbx similarity index 100% rename from test/src/main/resources/KeyFileDatabase.kdbx rename to test/src/test/resources/KeyFileDatabase.kdbx diff --git a/test/src/main/resources/KeyFileDatabase.key b/test/src/test/resources/KeyFileDatabase.key similarity index 95% rename from test/src/main/resources/KeyFileDatabase.key rename to test/src/test/resources/KeyFileDatabase.key index 6f63430b..aade8cbf 100755 --- a/test/src/main/resources/KeyFileDatabase.key +++ b/test/src/test/resources/KeyFileDatabase.key @@ -1,9 +1,9 @@ - - - - 1.00 - - - uE9L7SGiT0+PrSvZ0oIzaLjtpsiu50ciR/JXc44Pw24= - - + + + + 1.00 + + + uE9L7SGiT0+PrSvZ0oIzaLjtpsiu50ciR/JXc44Pw24= + + diff --git a/test/src/main/resources/NoPasswordWithKey.kdbx b/test/src/test/resources/NoPasswordWithKey.kdbx similarity index 100% rename from test/src/main/resources/NoPasswordWithKey.kdbx rename to test/src/test/resources/NoPasswordWithKey.kdbx diff --git a/test/src/main/resources/NoPasswordWithKey.key b/test/src/test/resources/NoPasswordWithKey.key similarity index 95% rename from test/src/main/resources/NoPasswordWithKey.key rename to test/src/test/resources/NoPasswordWithKey.key index 6fdee6b8..300da4d5 100755 --- a/test/src/main/resources/NoPasswordWithKey.key +++ b/test/src/test/resources/NoPasswordWithKey.key @@ -1,9 +1,9 @@ - - - - 1.00 - - - RJS69T/8tKYwO/fXPNGdtHlgLM5VfJmBDQLOlsg1AcE= - - + + + + 1.00 + + + RJS69T/8tKYwO/fXPNGdtHlgLM5VfJmBDQLOlsg1AcE= + + diff --git a/test/src/main/resources/V3-CustomIcon.kdbx b/test/src/test/resources/V3-CustomIcon.kdbx similarity index 100% rename from test/src/main/resources/V3-CustomIcon.kdbx rename to test/src/test/resources/V3-CustomIcon.kdbx diff --git a/test/src/main/resources/V4-AES-AES.kdbx b/test/src/test/resources/V4-AES-AES.kdbx similarity index 100% rename from test/src/main/resources/V4-AES-AES.kdbx rename to test/src/test/resources/V4-AES-AES.kdbx diff --git a/test/src/main/resources/V4-AES-Argon2-CustomIcon.kdbx b/test/src/test/resources/V4-AES-Argon2-CustomIcon.kdbx similarity index 100% rename from test/src/main/resources/V4-AES-Argon2-CustomIcon.kdbx rename to test/src/test/resources/V4-AES-Argon2-CustomIcon.kdbx diff --git a/test/src/main/resources/V4-AES-Argon2.kdbx b/test/src/test/resources/V4-AES-Argon2.kdbx similarity index 100% rename from test/src/main/resources/V4-AES-Argon2.kdbx rename to test/src/test/resources/V4-AES-Argon2.kdbx diff --git a/test/src/main/resources/V4-ChaCha20-AES.kdbx b/test/src/test/resources/V4-ChaCha20-AES.kdbx similarity index 100% rename from test/src/main/resources/V4-ChaCha20-AES.kdbx rename to test/src/test/resources/V4-ChaCha20-AES.kdbx diff --git a/test/src/main/resources/V4-ChaCha20-Argon2-Attachment.kdbx b/test/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx similarity index 100% rename from test/src/main/resources/V4-ChaCha20-Argon2-Attachment.kdbx rename to test/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx diff --git a/test/src/main/resources/V4-CustomIcon.kdbx b/test/src/test/resources/V4-CustomIcon.kdbx similarity index 100% rename from test/src/main/resources/V4-CustomIcon.kdbx rename to test/src/test/resources/V4-CustomIcon.kdbx diff --git a/test/src/main/resources/issue-27/bogus-timestamp.kdbx b/test/src/test/resources/issue-27/bogus-timestamp.kdbx similarity index 100% rename from test/src/main/resources/issue-27/bogus-timestamp.kdbx rename to test/src/test/resources/issue-27/bogus-timestamp.kdbx diff --git a/test/src/main/resources/issue-27/bogus-timestamp2.kdbx b/test/src/test/resources/issue-27/bogus-timestamp2.kdbx similarity index 100% rename from test/src/main/resources/issue-27/bogus-timestamp2.kdbx rename to test/src/test/resources/issue-27/bogus-timestamp2.kdbx diff --git a/test/src/main/resources/issue-38/Database/Database.kdbx b/test/src/test/resources/issue-38/Database/Database.kdbx similarity index 100% rename from test/src/main/resources/issue-38/Database/Database.kdbx rename to test/src/test/resources/issue-38/Database/Database.kdbx diff --git a/test/src/main/resources/issue-38/Database/Database.keyx b/test/src/test/resources/issue-38/Database/Database.keyx similarity index 100% rename from test/src/main/resources/issue-38/Database/Database.keyx rename to test/src/test/resources/issue-38/Database/Database.keyx diff --git a/test/src/main/resources/issue-38/Database/README.md b/test/src/test/resources/issue-38/Database/README.md similarity index 100% rename from test/src/main/resources/issue-38/Database/README.md rename to test/src/test/resources/issue-38/Database/README.md diff --git a/test/src/test/resources/issue-70/test1.kdbx b/test/src/test/resources/issue-70/test1.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..d27f37d1b1148ddb9245ddaa46b2f1746ae44804 GIT binary patch literal 1749 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 B--ZAH literal 0 HcmV?d00001 diff --git a/test/src/main/resources/kdb.key b/test/src/test/resources/kdb.key similarity index 100% rename from test/src/main/resources/kdb.key rename to test/src/test/resources/kdb.key diff --git a/test/src/main/resources/kdb_with_random_file.kdbx b/test/src/test/resources/kdb_with_random_file.kdbx similarity index 100% rename from test/src/main/resources/kdb_with_random_file.kdbx rename to test/src/test/resources/kdb_with_random_file.kdbx diff --git a/test/src/main/resources/kdbwithkey.kdb b/test/src/test/resources/kdbwithkey.kdb similarity index 100% rename from test/src/main/resources/kdbwithkey.kdb rename to test/src/test/resources/kdbwithkey.kdb diff --git a/test/src/main/resources/kdbxSample.xml b/test/src/test/resources/kdbxSample.xml similarity index 100% rename from test/src/main/resources/kdbxSample.xml rename to test/src/test/resources/kdbxSample.xml diff --git a/test/src/main/resources/kdbx_hash_test.kdbx b/test/src/test/resources/kdbx_hash_test.kdbx similarity index 100% rename from test/src/main/resources/kdbx_hash_test.kdbx rename to test/src/test/resources/kdbx_hash_test.kdbx diff --git a/test/src/main/resources/kdbx_hash_test.keyx b/test/src/test/resources/kdbx_hash_test.keyx similarity index 95% rename from test/src/main/resources/kdbx_hash_test.keyx rename to test/src/test/resources/kdbx_hash_test.keyx index 31d047f3..e5254af2 100755 --- a/test/src/main/resources/kdbx_hash_test.keyx +++ b/test/src/test/resources/kdbx_hash_test.keyx @@ -1,12 +1,12 @@ - - - - 2.0 - - - - 66F110CA E32995B5 EFA8E672 1E70C773 - 48F8E260 EEDD8744 93F41803 5BFDC27D - - + + + + 2.0 + + + + 66F110CA E32995B5 EFA8E672 1E70C773 + 48F8E260 EEDD8744 93F41803 5BFDC27D + + \ No newline at end of file diff --git a/test/src/main/resources/kdbx_hash_test_wrong_hash.keyx b/test/src/test/resources/kdbx_hash_test_wrong_hash.keyx similarity index 95% rename from test/src/main/resources/kdbx_hash_test_wrong_hash.keyx rename to test/src/test/resources/kdbx_hash_test_wrong_hash.keyx index 5c7b37e2..2bfa7c09 100755 --- a/test/src/main/resources/kdbx_hash_test_wrong_hash.keyx +++ b/test/src/test/resources/kdbx_hash_test_wrong_hash.keyx @@ -1,12 +1,12 @@ - - - - 2.0 - - - - 66F110CA E32995B5 EFA8E672 1E70C773 - 48F8E260 EEDD8744 93F41803 5BFDC27D - - + + + + 2.0 + + + + 66F110CA E32995B5 EFA8E672 1E70C773 + 48F8E260 EEDD8744 93F41803 5BFDC27D + + \ No newline at end of file diff --git a/test/src/main/resources/kdbx_keyfile32.kdbx b/test/src/test/resources/kdbx_keyfile32.kdbx similarity index 100% rename from test/src/main/resources/kdbx_keyfile32.kdbx rename to test/src/test/resources/kdbx_keyfile32.kdbx diff --git a/test/src/main/resources/kdbx_keyfile64.kdbx b/test/src/test/resources/kdbx_keyfile64.kdbx similarity index 100% rename from test/src/main/resources/kdbx_keyfile64.kdbx rename to test/src/test/resources/kdbx_keyfile64.kdbx diff --git a/test/src/main/resources/keyfile32 b/test/src/test/resources/keyfile32 similarity index 100% rename from test/src/main/resources/keyfile32 rename to test/src/test/resources/keyfile32 diff --git a/test/src/main/resources/keyfile64 b/test/src/test/resources/keyfile64 similarity index 100% rename from test/src/main/resources/keyfile64 rename to test/src/test/resources/keyfile64 diff --git a/test/src/main/resources/letter J.jpeg b/test/src/test/resources/letter J.jpeg similarity index 100% rename from test/src/main/resources/letter J.jpeg rename to test/src/test/resources/letter J.jpeg diff --git a/test/src/main/resources/letter L.jpeg b/test/src/test/resources/letter L.jpeg similarity index 100% rename from test/src/main/resources/letter L.jpeg rename to test/src/test/resources/letter L.jpeg diff --git a/test/src/main/resources/random_file b/test/src/test/resources/random_file similarity index 100% rename from test/src/main/resources/random_file rename to test/src/test/resources/random_file diff --git a/test/src/main/resources/test1.kdbx b/test/src/test/resources/test1.kdbx similarity index 100% rename from test/src/main/resources/test1.kdbx rename to test/src/test/resources/test1.kdbx diff --git a/test/src/main/resources/test123-AES-AES.kdbx b/test/src/test/resources/test123-AES-AES.kdbx similarity index 100% rename from test/src/main/resources/test123-AES-AES.kdbx rename to test/src/test/resources/test123-AES-AES.kdbx diff --git a/test/src/main/resources/test123-AES-Argon2.kdbx b/test/src/test/resources/test123-AES-Argon2.kdbx similarity index 100% rename from test/src/main/resources/test123-AES-Argon2.kdbx rename to test/src/test/resources/test123-AES-Argon2.kdbx diff --git a/test/src/main/resources/test123-ChaCha20-AES.kdbx b/test/src/test/resources/test123-ChaCha20-AES.kdbx similarity index 100% rename from test/src/main/resources/test123-ChaCha20-AES.kdbx rename to test/src/test/resources/test123-ChaCha20-AES.kdbx diff --git a/test/src/main/resources/test123-ChaCha20-Argon2.kdbx b/test/src/test/resources/test123-ChaCha20-Argon2.kdbx similarity index 100% rename from test/src/main/resources/test123-ChaCha20-Argon2.kdbx rename to test/src/test/resources/test123-ChaCha20-Argon2.kdbx diff --git a/test/src/main/resources/test123.kdb b/test/src/test/resources/test123.kdb similarity index 100% rename from test/src/main/resources/test123.kdb rename to test/src/test/resources/test123.kdb diff --git a/test/src/main/resources/test123.kdbx b/test/src/test/resources/test123.kdbx similarity index 100% rename from test/src/main/resources/test123.kdbx rename to test/src/test/resources/test123.kdbx diff --git a/test/src/main/resources/testDeleted.kdbx b/test/src/test/resources/testDeleted.kdbx similarity index 100% rename from test/src/main/resources/testDeleted.kdbx rename to test/src/test/resources/testDeleted.kdbx diff --git a/test/src/main/resources/xml/V4-AES-AES.xml b/test/src/test/resources/xml/V4-AES-AES.xml similarity index 100% rename from test/src/main/resources/xml/V4-AES-AES.xml rename to test/src/test/resources/xml/V4-AES-AES.xml diff --git a/test/src/main/resources/xml/V4-AES-Argon2.xml b/test/src/test/resources/xml/V4-AES-Argon2.xml similarity index 100% rename from test/src/main/resources/xml/V4-AES-Argon2.xml rename to test/src/test/resources/xml/V4-AES-Argon2.xml diff --git a/test/src/main/resources/xml/V4-ChaCha20-AES.xml b/test/src/test/resources/xml/V4-ChaCha20-AES.xml similarity index 100% rename from test/src/main/resources/xml/V4-ChaCha20-AES.xml rename to test/src/test/resources/xml/V4-ChaCha20-AES.xml diff --git a/test/src/main/resources/xml/V4-ChaCha20-Argon2-Attachment.xml b/test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml similarity index 100% rename from test/src/main/resources/xml/V4-ChaCha20-Argon2-Attachment.xml rename to test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml diff --git a/util/pom.xml b/util/pom.xml new file mode 100644 index 00000000..234243b6 --- /dev/null +++ b/util/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + KeePassJava2.parent + org.linguafranca.pwdb + 3.0.0-SNAPSHOT + ../pom.xml + + + util + PWDB :: Util + Contains some helpful underlying classes for examples and testing. + + + true + + + + + junit + junit + provided + + + org.slf4j + slf4j-api + provided + + + \ No newline at end of file diff --git a/test/src/main/java/org/linguafranca/util/HexViewer.java b/util/src/main/java/org/linguafranca/util/HexViewer.java similarity index 79% rename from test/src/main/java/org/linguafranca/util/HexViewer.java rename to util/src/main/java/org/linguafranca/util/HexViewer.java index 2ec49e0c..b9c17353 100644 --- a/test/src/main/java/org/linguafranca/util/HexViewer.java +++ b/util/src/main/java/org/linguafranca/util/HexViewer.java @@ -13,14 +13,6 @@ public class HexViewer { static PrintStream printStream = getTestPrintStream(); - public static void main(String[] args) throws IOException { - - - try (InputStream is = HexViewer.class.getClassLoader().getResourceAsStream("V4-AES-Argon2-CustomIcon.kdbx");) { - list(is); - } - } - public static void list(InputStream is) throws IOException { for (int i = 0; i < 32; i++) { byte[] buf = new byte [16]; diff --git a/database/src/main/java/org/linguafranca/util/MavenRunListener.java b/util/src/main/java/org/linguafranca/util/MavenRunListener.java similarity index 100% rename from database/src/main/java/org/linguafranca/util/MavenRunListener.java rename to util/src/main/java/org/linguafranca/util/MavenRunListener.java diff --git a/util/src/main/java/org/linguafranca/util/TestUtil.java b/util/src/main/java/org/linguafranca/util/TestUtil.java new file mode 100644 index 00000000..f6883ecf --- /dev/null +++ b/util/src/main/java/org/linguafranca/util/TestUtil.java @@ -0,0 +1,23 @@ +package org.linguafranca.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.PrintStream; + +public class TestUtil { + + public static class NullOutputStream extends OutputStream { + + @Override + public void write(int b) throws IOException {} + } + /** + * set system property to suppress output from tests + * @return if "inhibitConsoleOutput" has been set, e.g. in a profile + */ + public static PrintStream getTestPrintStream() { + return Boolean.getBoolean("inhibitConsoleOutput") ? + new PrintStream(new NullOutputStream()) : + new PrintStream(System.out); + } +} From b759b1f40bf09962073030cdd89618153ba6ac15 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 22 Nov 2024 15:20:12 +0000 Subject: [PATCH 18/66] update changelog --- CHANGELOG.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ca68e6df..d5707d64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.com) and [Semantic Versioning](http://semver.org/spec/v2.0.0.html) -## 2024-09-13 [2.3-SNAPSHOT] +## 2024-11-22 [3.0.0-SNAPSHOT] ### Changed @@ -11,11 +11,7 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.co - remove DOM database - remove complicated generics on database classes -### Added - -- added a service provider interface for extensibility - -## 2024-09-10 [2.2.3-SNAPSHOT] +## 2024-11-22 [2.2.3-SNAPSHOT] ### Added @@ -26,6 +22,11 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.co - Jackson implementation supports this interface - Other implementations throw exceptions appropriately as unsupported +- Module restructure to avoid test files, junit being dragged along in deployed JAR (Issue-71) + +### Fixed + +KDBX file format version 4.1 now supported (Issue-70) (in Jackson, not Simple) ## [2.2.2] 2024-09-06 From 2150e55285c446edd3663a04040e7ed8827b0fbd Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 22 Nov 2024 16:10:20 +0000 Subject: [PATCH 19/66] minor tweaks --- database/src/main/java/org/linguafranca/pwdb/Entry.java | 3 ++- .../main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java | 2 +- example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java | 6 ++---- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 1da7d4f9..1f69b842 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -248,6 +248,7 @@ interface Matcher { *

Implementations should Touch LastAccessedTime when this method is called. * * @return a password + * @deprecated use {@link #getPropertyValue(String)} with {@link #STANDARD_PROPERTY_NAME_PASSWORD} */ @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated @@ -259,7 +260,7 @@ interface Matcher { *

Implementations should Touch LastModifiedTime when this method is called. * * @param pass a password - * @deprecated use {@link #setPropertyValue(String, PropertyValue)} + * @deprecated use {@link #setPropertyValue(String, PropertyValue)} with {@link #STANDARD_PROPERTY_NAME_PASSWORD} */ @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index f05c9a4a..5b3e93e3 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -30,7 +30,7 @@ public Database load(KdbxCreds creds, InputStream inputStream) throws Exception } } - public static void testDb (DbLoader loader, String label, int loads, int iterations) throws Exception { + private static void testDb (DbLoader loader, String label, int loads, int iterations) throws Exception { KdbxCreds creds = new KdbxCreds("123".getBytes()); long start = System.currentTimeMillis(); for (int i=0; i < loads; i++) { diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java index 941768b0..1174dbb9 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java @@ -11,20 +11,18 @@ import java.io.*; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; public class Util { - List implementations = new ArrayList<>(Arrays.asList(KdbxDatabase.class)); + List> implementations = List.of(KdbxDatabase.class); @FunctionalInterface public interface DatabaseLoader { Database load(Credentials c, InputStream i) throws IOException; } - List dbLoader = Arrays.asList(KdbxDatabase::load); + List dbLoader = List.of(KdbxDatabase::load); public static InputStream getDecryptedInputStream (String resourceName, Credentials credentials) throws IOException { return getDecryptedInputStream(resourceName, credentials, new KdbxHeader()); From 28ca9b03554feb544751a5821549872a1b90d20d Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 15:48:33 +0000 Subject: [PATCH 20/66] logback dependency update --- pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index aff20c01..424d45b6 100644 --- a/pom.xml +++ b/pom.xml @@ -49,7 +49,7 @@ scm:git:git://github.com/jorabin/KeePassJava2.git scm:git:git@github.com:jorabin/KeePassJava2.git https://github.com/jorabin/KeePassJava2 - KeePassJava2-2.2.2 + KeePassJava2-3.0.0 @@ -235,14 +235,14 @@ org.slf4j slf4j-api - 2.0.12 + 2.0.16 ch.qos.logback logback-classic - 1.3.14 + 1.3.15 commons-codec From ad835ec7e909787de5b452945cca3635838b6d85 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 16:41:39 +0000 Subject: [PATCH 21/66] Custom Data and missing properties added --- .../pwdb/kdbx/validation/Issue70Test.java | 13 +++++++++++++ .../pwdb/kdbx/jackson/KdbxEntry.java | 15 ++++++++++++++- .../pwdb/kdbx/jackson/KdbxGroup.java | 7 +++++++ .../kdbx/jackson/KdbxSerializableDatabase.java | 12 ++++++++++++ .../pwdb/kdbx/jackson/model/KeePassFile.java | 6 ++++++ .../pwdb/format/KdbxStreamFormat.java | 2 +- .../test/resources/issue-70/test-hugoo10.kdbx | Bin 0 -> 2215 bytes 7 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 test/src/test/resources/issue-70/test-hugoo10.kdbx diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java index 2d2a694a..9819d53f 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java @@ -16,7 +16,11 @@ public class Issue70Test { public static final KdbxCreds CREDENTIALS1 = new KdbxCreds("KeePassJava2".getBytes()); public static final String TEST_RESOURCE2 = "Database-4.1-123.kdbx"; public static final KdbxCreds CREDENTIALS2 = new KdbxCreds("123".getBytes()); + public static final String TEST_RESOURCE3 = "issue-70/test-hugoo10.kdbx"; + public static final KdbxCreds CREDENTIALS3 = new KdbxCreds("test".getBytes()); + + /* custom data missing from meta */ @Test public void testCustomData() throws IOException { try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { @@ -30,4 +34,13 @@ public void testFileFormat_4_1() throws IOException { KdbxDatabase database = KdbxDatabase.load(CREDENTIALS2, inputStream); } } + + /* custom data missing from entry definition */ + @Test + public void testCustomData2() throws IOException { + try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE3)){ + KdbxDatabase database = KdbxDatabase.load(CREDENTIALS3, inputStream); + } + } + } \ No newline at end of file diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index c12f21fb..724740a7 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -28,6 +28,8 @@ import org.linguafranca.pwdb.base.AbstractEntry; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; +import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; +import org.linguafranca.pwdb.kdbx.jackson.converter.StringToBooleanConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.UUIDToBase64Converter; import org.linguafranca.pwdb.kdbx.jackson.model.KeePassFile; @@ -55,11 +57,13 @@ "foregroundColor", "backgroundColor", "overrideURL", + "previousParentGroup", "tags", "times", "string", "binary", "autoType", + "customData", "history", }) @@ -122,7 +126,13 @@ public static KdbxEntry createEntry(KdbxDatabase database) { @JacksonXmlProperty(localName = "Tags") protected String tags; - + + + @JacksonXmlProperty(localName = "QualityCheck") + @JsonDeserialize(converter = StringToBooleanConverter.class) + @JsonSerialize(converter = BooleanToStringConverter.class) + protected Boolean qualityCheck; + @JacksonXmlProperty(localName = "Times") protected Times times; @@ -137,6 +147,9 @@ public static KdbxEntry createEntry(KdbxDatabase database) { @JacksonXmlProperty(localName = "AutoType") protected AutoType autoType; + @JacksonXmlProperty(localName = "CustomData") + protected KeePassFile.CustomData customData; + @JacksonXmlProperty(localName = "History") /* Workaround jackson */ protected KdbxHistory history; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 5b0c5942..6a39c0f3 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -32,6 +32,7 @@ import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.StringToBooleanConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.UUIDToBase64Converter; +import org.linguafranca.pwdb.kdbx.jackson.model.KeePassFile; import org.linguafranca.pwdb.kdbx.jackson.model.Times; import java.util.ArrayList; @@ -51,6 +52,9 @@ "enableAutoType", "enableSearching", "lastTopVisibleEntry", +"previousParentGroup", +"tags", +"customData", "entry", "group", }) @@ -110,6 +114,9 @@ public class KdbxGroup extends AbstractGroup{ @JacksonXmlProperty(localName = "Tags") protected String tags; + @JacksonXmlProperty(localName = "CustomData") + protected KeePassFile.CustomData customData; + @JacksonXmlProperty(localName = "Entry") /* Workaround jackson */ @JacksonXmlElementWrapper(useWrapping = false) protected List entries; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index 2faf44a0..016fb200 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -17,6 +17,7 @@ import com.ctc.wstx.api.WstxInputProperties; import com.ctc.wstx.api.WstxOutputProperties; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.databind.module.SimpleModule; @@ -40,6 +41,13 @@ public class KdbxSerializableDatabase implements SerializableDatabase { + /** + * By default, deserialization will fail if an unknown property is found. Historically, + * this has been because the file mapping is incomplete, rather than the incoming file being wrong. + * So use this feature with caution, if at all. + */ + public static boolean FAIL_ON_UNKNOWN_PROPERTIES = true; + public KeePassFile keePassFile; private StreamEncryptor encryptor; @@ -68,6 +76,9 @@ public KdbxSerializableDatabase(KeePassFile keePassFile) { @Override public KdbxSerializableDatabase load(InputStream inputStream) throws IOException { XmlMapper mapper = new XmlMapper(); + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, + KdbxSerializableDatabase.FAIL_ON_UNKNOWN_PROPERTIES); + SimpleModule module = new SimpleModule(); module.addDeserializer(PropertyValue.class, new ValueDeserializer(encryptor, propertyValueStrategy)); mapper.registerModule(module); @@ -141,6 +152,7 @@ public static void addBinary(KeePassFile keePassFile, int index, byte[] payload) keePassFile.meta.binaries.add(newBin); } + // TODO this gets binary at index but does not get binary with ID @Override public byte[] getBinary(int index) { KeePassFile.Binary binary = keePassFile.meta.binaries.get(index); diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java index 8ffdf07e..116a315e 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java @@ -83,6 +83,7 @@ public Root() { "masterKeyChanged", "masterKeyChangeRec", "masterKeyChangeForce", + "masterKeyChangeForceOnce", "memoryProtection", "customIcons", "recycleBinEnabled", @@ -150,6 +151,11 @@ public Meta() { @JacksonXmlProperty(localName = "MasterKeyChangeForce") protected int masterKeyChangeForce; + @JacksonXmlProperty(localName = "MasterKeyChangeForceOnce") + @JsonDeserialize(converter = StringToBooleanConverter.class) + @JsonSerialize(converter = BooleanToStringConverter.class) + protected Boolean masterKeyChangeForceOnce; + @JacksonXmlProperty(localName = "MemoryProtection") public KeePassFile.MemoryProtection memoryProtection; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java index 66039456..da0fb9bf 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java @@ -80,7 +80,6 @@ public void load(SerializableDatabase serializableDatabase, Credentials credenti public void save(SerializableDatabase serializableDatabase, Credentials credentials, OutputStream outputStream) throws IOException { Helpers.isV4.set(kdbxHeader.getVersion() == 4); if (kdbxHeader.getVersion() == 4) { - // TODO this assumes that the indexes start from 0 and are in sequence ... for (int a = 0; a < serializableDatabase.getBinaryCount(); a++) { int attachmentLength = serializableDatabase.getBinary(a).length; byte[] binary = new byte[attachmentLength + 1]; @@ -88,6 +87,7 @@ public void save(SerializableDatabase serializableDatabase, Credentials credenti System.arraycopy(serializableDatabase.getBinary(a),0, binary, 1, attachmentLength); kdbxHeader.addBinary(binary); } + // TODO the binaries should now be removed so they don't get serialized in XML } try (OutputStream encryptedOutputStream = KdbxSerializer.createEncryptedOutputStream(credentials, kdbxHeader, outputStream)) { diff --git a/test/src/test/resources/issue-70/test-hugoo10.kdbx b/test/src/test/resources/issue-70/test-hugoo10.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..1eb0185953d24c38828a821c93b307d22ee49a08 GIT binary patch literal 2215 zcmV;Y2w3+6*`k_f`%AR}00aO65C8xGF~RcYzi~rQzE}kzYW!ON0|Wp70096100bZa z0020y2U)2QR-B5>7Y8Ih0KqgrLe80?s7)jO`j*Fv{+J70000000YU`;001OaRY^n; z0002V+4GuWibP<)bPWi>icjhV0RR91QV0M50Kg{+00000073x(002`U0000Pgs;aA{>0T!fbv953 z002v{0y2RNl2MAm(klLFs}CT$c6TZ2ULeETfSCLC7h@#GLkc zKNgA9mU}H1RI;p*=bdg+d82sCGB2tAaFKPSuTs<3k5F*;zRDgexi`-^Oac7`MtmRM zbXSfZH}zGc#t-_4)?qyL?w`&z`_NzopETWqdJZ%AJ)Bi9 zqhfSHB~p1u0QHBqfCydJ(?%ptF;?$hI_9U9UF+M2^_TC5zj$T4ZzTmdr$+r0`vW>a zLEZfTv%)PWbMCv7X=xlxL%$^ys3s{|N&HyIps;YI;A}#Q{|Kr^VYk_s(hDrHrk`^) zB2%{p-*^ov1FaibM#0H1?O}DzH)B*R;E+enYZ1>s4cWyFxEZP)IUkb@Q7u*uowJ5E->>N zcKY=_r#LQVE`cdTBkyRA|7lX0aiYRODNocZ$Iq3Yk9V}}i9hnbYNwA_(BNDJ9~4`G zSWrIkfKUpfsJri*+EltxK9t(>GS)4xsFV3THs84a!j67g_pM}XEYe3f>icRH3XphN zZFqs+>t?yc1v(=UZnRrwoTkECwa_1!68PL@^Tx{iChf87a-r)&Yw(Pzel>s>Ko&=9 z?ugnJfEiI?N6lNJe13;iL&?2)bmX#(*tsZA#`GN{(W0W_Ogt9+{dwD8*8nQrdXV)M z|C%H8mv>wdxg-cd6x-p$8uC;(hL!iQs-^T{-6x1?;v0~`y$K?XHfp}huL4Z#)~FsM zTDcZGWp*hyc>gvWDaB8OP}#@DjejuUB#>#L0i@d12NM_}zl3bj?E&ReiJlPW79fPh zN;yOv+%aoZZD}vm$zbrp-1}!rL22or1z~6ra{J+@#VqI2F%z%As)xesKd3|cyOKB_ zZAOPEy%9@Y$yKT0b(A7S-S?CpvS(>$+qw;`9D+wm~kB&ZCXO$tHLBhbi1gRCr0$94*~T1#Zh#|v7{jsCraU>-j?6J*LB_RL;vFdBnM2P!%_iE<(s0!KDqK}__)*r~GyzU}$xA(|4xk56DKF9=j)a4~Q zKy(XHQzWnZ%VT+cV-StCD{nGiGBX<%2^3wm*pT}K5R1SX&rjU|05dp9{V%km0|4~V zh%v+`6=+3?j;$pCdhEj)16FDOKgQZfrt`XuHOKTnEXsel(EnV5S{QtK7`hM_)7k=5 z@dUv5c&VIC{Brj6F3TD~mm=F>-J{O6){^-pBaTdKlMoT3TK_t^1Mrtk;*t|&7TE8* zRxJyDMP8x@->SrIK}W`e^a!7YRMk$J9|hF)n(IYG{#iyHbZh># z$%bAGcDGvERhr^EI&8tbqkXRsaAr8yyzrt4Ep$WN(!Hcg6x&v2BrW!pek1=y3IIu{ zpvlt2Z^V+%I`F@Am=}-dq9v~i&`YQ^D^~{FG1-nk?f*y(argRIM<`)N6TcDh2-^oa pcCE?j6MmjJtqi1pBu)r%=a3>o_F~2Y#iUQa+xl>IW7z-z006>0F4F)2 literal 0 HcmV?d00001 From 5426d37d865a7a0bc64ca2af153a3e89f0a54c69 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 16:47:12 +0000 Subject: [PATCH 22/66] Tweaks to viewing XML --- .../org/linguafranca/pwdb/kdbx/ChooseFile.java | 15 ++++++++++----- .../java/org/linguafranca/pwdb/kdbx/SaxParse.java | 1 + 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java index 761a52a3..1e78822b 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java @@ -2,6 +2,8 @@ import com.google.common.base.Strings; import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -9,8 +11,10 @@ import javax.swing.filechooser.FileFilter; import java.io.*; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Paths; +import static org.linguafranca.pwdb.kdbx.Util.streamToString; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** @@ -18,8 +22,6 @@ */ public class ChooseFile { - OutputStream outputStream = getTestPrintStream(); - public static void main(String[] args) throws IOException { ChooseFile cf = new ChooseFile(); cf.choose(); @@ -66,9 +68,12 @@ public String getDescription() { return; } logger.info("Opening {}", fc.getSelectedFile().getPath()); - Util.listXml(fc.getSelectedFile().getName(), - new KdbxCreds(s.getBytes()), - new PrintWriter(outputStream)); + try (InputStream is = Files.newInputStream(Paths.get(fc.getSelectedFile().getPath()))) { + InputStream ss = KdbxSerializer.createUnencryptedInputStream(new KdbxCreds(s.getBytes()), new KdbxHeader(), is); + System.out.println(streamToString(ss)); + System.out.println(); + System.out.flush(); + } /* try (InputStream is = Files.newInputStream(Paths.get(fc.getSelectedFile().getPath()))) { HexViewer.list(is); }*/ diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java index 0c15cb58..b0c3e7de 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java @@ -92,5 +92,6 @@ public void characters(char[] ch, int start, int length) { InputSource xmlInputSource = new InputSource(decryptedInputStream); xmlReader.parse(xmlInputSource); } + writer.flush(); } } From 2cf9e2756e2aed5aff50f2ec26252ced64f13bf4 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 16:50:53 +0000 Subject: [PATCH 23/66] Documents updated --- CHANGELOG.md | 3 ++- PropertyValueProtection.md | 4 ++-- readme.md | 36 +++++++++++++++++++++++++++--------- 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d5707d64..16f977b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,8 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.co - Jackson implementation supports this interface - Other implementations throw exceptions appropriately as unsupported -- Module restructure to avoid test files, junit being dragged along in deployed JAR (Issue-71) +- [Issue #70] Improved support for KDBX 4.1 format (Jackson only) +- [ISSUE #71] Module restructure to avoid test files, junit being pulled in unnecessarily ### Fixed diff --git a/PropertyValueProtection.md b/PropertyValueProtection.md index ccb6f87d..39e130ef 100644 --- a/PropertyValueProtection.md +++ b/PropertyValueProtection.md @@ -8,7 +8,7 @@ that have sensitive values are called "Protected Properties". KDBX files contain a list of the standard properties and whether they are to be treated as protected by default. The standard properties include Password, which is so treated. There doesn't seem to be a way of updating this list in the Windows KeePass implementation, and in any case documentation says that the -list is updated to default after load of a database, so it seems that it ignored. +list is updated to default after load of a database, so it seems that it is ignored. Individual properties can be marked as "protect in memory", but there is no way to indicate that all properties of that name should be protected. @@ -63,7 +63,7 @@ luck trying to avoid strings at all. Likewise, if you collect passwords from a d After the deserialization process, the passwords are stored in RAM. Therefore: **Hashing the password is not feasible**, as the hash function is a one-way process. -This means that once a password is hashed, it cannot be reverted back to its original form, +This means that once a password is hashed, it cannot be reverted to its original form, which makes hashing unsuitable in this context. Since we need to retrieve and view the saved passwords (as per the requirements), hashing does not meet the objective. diff --git a/readme.md b/readme.md index 13242771..9fad5c8d 100644 --- a/readme.md +++ b/readme.md @@ -10,7 +10,7 @@ ![alt text](https://badgen.net/badge/Branch/v3/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.3-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/v3) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/fe9059ac4d384b929f452149b9246658)](https://app.codacy.com/gh/jorabin/KeePassJava2/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) -A Java 8 API for databases compatible with the renowned [KeePass](http://keepass.info) password +A Java 11 API for databases compatible with the renowned [KeePass](http://keepass.info) password safe for Windows. This is a "headless" implementation - if you want something with a UI then [KeePassXC](https://keepassxc.org/) and [KeePassDX](https://www.keepassdx.com/) could be just the things for you. @@ -41,12 +41,12 @@ It is licensed under the Apache 2 License and is currently usable. ## Current Status -This version is 3.0.0-SNAPSHOT and is not backwards compatible with 2.2 and earlier versions. +This version is 3.0.0-SNAPSHOT and is not backwards compatible with 2.3 and earlier versions. -The current released code is version 2.2.2 - released to Maven September 2024. This is on the main branch. See [Build from Source](#build-from-source) +The current released code is version 2.2.3 - released to Maven January 2025. This is on the main branch. See [Build from Source](#build-from-source) -Key updates relative to 2.1 -- Java 8 (dependencies no longer support Java 7) +Key updates relative to 2.x +- Java 11 - File format version 4 support - with Argon2 - Removal of SimpleXML, JAXB and JAXB database implementations - Implemented pluggable (protected) data storage model @@ -61,11 +61,19 @@ See the [changelog](CHANGELOG.md) for more details. ### Release -The composite POM for the last release (2.2.2), Java 8 compatible, is +The POM for the last release (2.2.3), Java 8 compatible, is + + org.linguafranca.pwdb + KeePassJava2-jackson + 2.2.3 + +at Maven Central. This provides access to the Jackson based implementation, +which is now the recommended implementation. There is also a composite POM (deprecated) that +provides access to all implementations (see [below](#database-implementations) for discussion). org.linguafranca.pwdb KeePassJava2 - 2.2.2 + 2.2.3 at Maven Central. Note that the artifactId has become Camel Case from release 2.1.x onwards. @@ -75,7 +83,7 @@ Snapshot builds are erratically available at [Sonatype](https://oss.sonatype.org org.linguafranca.pwdb KeePassJava2 - 2.2.3-SNAPSHOT + 3.0.0-SNAPSHOT with appropriate `` entry, like: @@ -179,11 +187,21 @@ KeePass formats in the following locations: KeePass - or more specifically its file format KDBX - is an XML based format, so one of the main tasks is serializing and deserializing XML. Over time (KeePassJava2 was originally released in 2014) approaches to Java and XML have been a bit mysterious. However, Jackson has now been chosen as the -underlying framework for implementation of KeePassJava2. From 2.2.3 a single KDBX implementation is available. +underlying framework for implementation of KeePassJava2. From 3.0.0 a single KDBX implementation is available. There are several other database implementations which will be maintained for bug-fix purposes only, with a view to being withdrawn, since they perform badly and/or depend on obsolete technology. +- `SimpleXML` - no longer maintained, does not work with Java 17 and up +- `JAXB` - this causes problems with `javax` and `jakarta` namespaces, it's not worth maintaining + as it offers no compelling performance or other advantage +- `DOM` the was the original implementation and validates the fact that DOM based implementations + are slow. That said, if you want to load a database and then save it while maintaining whatever quirks + existed in the original database then this is the one. + +Aside from dependencies on underlying frameworks, different implementations have varying characteristics, primarily speed. This is assessed +by [this test](https://github.com/jorabin/KeePassJava2/blob/master/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java) in the module `examples`. + ## Dependencies Aside from the JRE, at release 3.0.0, the API depends on: From 44dea14d50d12c24ca56b7f2d2d31de4f0e0f95c Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 16:57:05 +0000 Subject: [PATCH 24/66] XSDs updated --- KDBX.3.1.xsd => XSD/KDBX.3.1.xsd | 0 XSD/KDBX.4.1.reichl.xsd | 492 ++++++++++++++++++++++++++ XSD/KDBX.4.1.xsd | 583 +++++++++++++++++++++++++++++++ KDBX.4.xsd => XSD/KDBX.4.xsd | 0 readme.md | 8 +- 5 files changed, 1079 insertions(+), 4 deletions(-) rename KDBX.3.1.xsd => XSD/KDBX.3.1.xsd (100%) create mode 100644 XSD/KDBX.4.1.reichl.xsd create mode 100644 XSD/KDBX.4.1.xsd rename KDBX.4.xsd => XSD/KDBX.4.xsd (100%) diff --git a/KDBX.3.1.xsd b/XSD/KDBX.3.1.xsd similarity index 100% rename from KDBX.3.1.xsd rename to XSD/KDBX.3.1.xsd diff --git a/XSD/KDBX.4.1.reichl.xsd b/XSD/KDBX.4.1.reichl.xsd new file mode 100644 index 00000000..a2f4780d --- /dev/null +++ b/XSD/KDBX.4.1.reichl.xsd @@ -0,0 +1,492 @@ + + + + KDBX 4.1 XML Schema. Copyright (C) 2007-2025 Dominik Reichl. https://keepass.info/help/kb/kdbx.html + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the application that has generated the XML document. + + + + + Hash of the (unencrypted) header of a KDBX file. Used only in KDBX files prior to version 4. In KDBX ≥ 4, the integrity and the authenticity are ensured using a HMAC instead (see KDBX spec.). + + + + + Last date/time when a database setting (stored in the Meta element) has been changed. + + + + + + + + + User name that is used by default for new entries. + + + + + + Number of days until history entries are deleted in a database maintenance operation. + + + + + Database color. The user interface can colorize elements with this color in order to allow the user to quickly identify the database. + + + + + Last date/time when the master key has been changed. + + + + + Number of days until a change of the master key is recommended. -1 means never. + + + + + + + + + + Number of days until a change of the master key is enforced. -1 means never. + + + + + + + + + + If true, a change of the master key should be enforced once directly after the user opens the database. + + + + + + + + + + + + + + UUID of the group that is used as recycle bin. Zero UUID = create new group when necessary. + + + + + + + + Maximum number of history entries that each entry may have. -1 means unlimited. + + + + + + + + + + Maximum estimated size in bytes (in the process memory) of the history of each entry. -1 means unlimited. + + + + + + + + + + + + In this element, the content of each binary is stored. Used only in unencrypted XML files and in KDBX files prior to version 4. In KDBX ≥ 4, binaries are stored in the inner header (encrypted) instead. + + + + + + + + + + + + + + + + + Process memory protection settings, describing which standard fields should be protected. KeePass resets these settings to their default values after opening a database. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Reference to a binary content stored in the inner header (KDBX file) or in the Meta/Binaries element (unencrypted XML file). + + + + + + + + + + + + + + + + + + Used in a KDBX file. + + + + + Used in an unencrypted XML file. + + + + + + + + + + + If the attribute is true, the content of the element has been encrypted (and Base64-encoded). See "inner encryption" on https://keepass.info/help/kb/kdbx.html + + + + + + Custom data item (key/value pair) for plugins/ports. The key should be unique, e.g. "PluginName_ItemName". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When the user deletes an object (group, entry, ...), an item is created in this list. When synchronizing/merging database files, this information can be used to decide whether an object has been deleted. + + + + + + + + + + + + + + + + + + + In general, last access times are not reliable, because an access is not considered to be a database change. See the UIFlags value 0x20000: https://keepass.info/help/v2_dev/customize.html#uiflags + + + + + + + Cf. LastAccessTime. + + + + + Last date/time when the object has been moved (within its parent group or to a different group). This is used by the synchronization algorithm to determine the latest location of the object. + + + + + + + + + + + + + See the folder "Ext/Images_Client_HighRes" in the KeePass source code package. + + + + + Reference to a custom icon stored in the KeePassFile/Meta/CustomIcons element. If non-zero, it overrides IconID. + + + + + + Specifies whether the group is displayed as expanded in the user interface. + + + + + + + + + UUID of the group in which the current group was stored previously. This information can for instance be used by a recycle bin restoration command. + + + + + Tags associated with the group, separated using ';'. https://keepass.info/help/v2/entry.html#tags + + + + + + + + + + + + + + See TGroup/IconID. + + + + + See TGroup/CustomIconUUID. + + + + + + + https://keepass.info/help/base/autourl.html#override + + + + + https://keepass.info/help/v2/entry.html#gen https://keepass.info/help/kb/pw_quality_est.html + + + + + See TGroup/Tags. + + + + + See TGroup/PreviousParentGroup. + + + + + + + + + + https://keepass.info/help/v2/entry.html#hst + + + + + + + + + + + + https://keepass.info/help/base/autotype.html + + + + + + + + + + No obfuscation. + + + + + Two-channel auto-type obfuscation. https://keepass.info/help/v2/autotype_obfuscation.html + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + A hexadecimal CSS color of the form "#RRGGBB". For example, "#FFFF00" is yellow. An empty string means to use the default value (chosen by the application, suitable for the current UI). + + + + + + + + + + + + + + + + + In a KDBX ≥ 4 file, dates are stored as the number of seconds (Int64) elapsed since 0001-01-01 00:00:00 UTC encoded using Base64. In an unencrypted XML file, dates are stored as xs:dateTime. + + + + + + + + + + + + + + + + + A 128-bit UUID encoded using Base64. + + + + + + \ No newline at end of file diff --git a/XSD/KDBX.4.1.xsd b/XSD/KDBX.4.1.xsd new file mode 100644 index 00000000..419eddd8 --- /dev/null +++ b/XSD/KDBX.4.1.xsd @@ -0,0 +1,583 @@ + + + + + + The KDBX 4.1 XML format as used by KeePass 2.x compatible programs. + + This is an update of earlier versions of the schema for versions 3.1 and 4 to be found at ../KDBX.{v}.xsd + + The KDBX schema is documented (with somewhat different conventions) at + https://keepass.info/help/download/KDBX_XML.xsd. This schema was originally created as a) that XSD did + not exist at the time b) this schema was originally created to provide for JAXB bindings and was + tailored for that purpose. + + In this version, artefacts that existed only to allow for JAXB purposes, for example abstract "parented" + elements to make it easier to add transient data in Java, have been removed. Also some correlation was + carried out with the XSD at keepass.info, which as mentioned did not originally exist at the time this + file was originally created. xs:sequence has been changed to xs:all where appropriate. + + Conventions in this schema: + + KeePassFile, Group, Entry, Times and History are top level elements as they may be useful for + creating fragments or something. + + There are types for almost everything that contains documentation as to its purpose + and how it's used and if there are restrictions on the values. + + Everything else is simply included inline for the sake of brevity. Undoubtedly + more documentation could most usefully be added. + + Some elements are probably optional but are not marked as such. In real life they may be serialised + as empty elements and some are serialised with content "null". Actual serialisations will depend on + the view that various implementations have taken, so flexibility is required when loading. Historically + this was particularly true of boolean values. + + + + + + + + The root element. + + + + + + + This contains database settings and also contains information + about the last state of the program that used the database. Presumably assuming + that the same program will be used to open it again, or that a different program + would want/need the same settings. + + + + + + The name of an application. This might be an application + that created the original document or it could be an application that + altered it at some point. + + + + + + Header hash removed from version 4. Hashes now contained + in the kdbx header itself. + + + + + + The last time something in the enclosing meta element changed. + + + + + + + + + + + + + + + + + + When the XML database is loaded into memory, sensitive + fields will be encrypted in that stream. This element specifies which of the + standardFields are encrypted in that stream, which is decrypted using Salsa20 + for KDBX3 (or ChaCha20 by default for V4) and the InnerRandomStream encryption header. + + ./Entry/String/Value/@Protected signifies that the value of a stringField is + so encrypted and applies also to customFields. + + Attachments are optionally memory protected in V4. + + + + + + + + + + + + + + + + + + + + + + + + + Used to store binary values in version 3.1 - in version 4 + such values are now transferred as part of the KDBX inner headers. + + + + + + + + + + + + + + This is the root group which contains everything. + + + + + + When the user permanently deletes an wrappedEntry or a group, + an item is created in this node. This allows the synchronization function + to decide whether an object has been deleted or is just missing in one of + the databases that are being synchronized. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + When it was last updated, resulting in an update to History if that is + active. + + + + + + When it was originally created. + + + + + When was last used (read). + + + + + When it expires if it expires. + + + + + True if it expires. + + + + + How often the object has been used (e.g. by copying data of it to the + clipboard, performing auto-type with it, etc.). However, a change to the usage count is + not considered to be a database change (i.e. the database is not marked as changed by the + UI), thus this value is typically incorrect (and the same applies to the last access + time). + + + + + + The date/time when the object was last moved. This allows the + synchronization function to correctly determine the most recent location of the + object. + + + + + + + + + + + + + + + + + + + + + I think the preferred values are "True" and "False", but for the sake of accommodating + various non-conformant applications ... accept a wider variety. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + This is ISO Date time stored as UTC (Z time zone). + + In KDBX Version 4 this becomes a base64 encoded offset in seconds + from 0001-01-01T00:00:00Z - if stored in a KDBX encoded file, but + is a xs:dateTime otherwise. There is no attribute to say what the format is + so be prepared to do either. + + + + + + + + + + + + + + XSD doesn't do comma (or semicolon) separated lists, so we can't model this handily. + + Assume that the format is something like [{token}{[,;] token}*]+ where white space is ignored except + as part of the name of the token. + + See https://keepass.info/help/v2/entry.html#tags + + + + + + + + + This is where the values of the database are actually stored. You can have String valued + fields, and you can have Binary valued fields. There are "Default" String fields (username and so on) and + there are custom string fields (custom only in that their names are not the names of default string + fields). Not really clear whether the keys are case-sensitive. + + Note that the "Protected" attribute is used in encrypted KDBX files and "ProtectInMemory" is used + in unencrypted XML files. + + + + + + + + + + + + + + + + + + + + If derived from a file then the Key element conventionally contains the file name. + I think it would be useful to add a mime type on this ... + + The Value element is the value of /./meta/binaries/binary/@ID that is referenced from this + binary (V3 and XML formatted documents). Or in the inner header of a KDBX file. + + + + + + + + + + + + + + + + + An index to one of the 69 built in icons. + + + + + + + + + + + + Simply put: "#rrggbb". + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Specifies the override URL, as documented at + http://keepass.info/help/base/autourl.html#override + + + + + + + + + + Stores everything related to auto-type for this wrappedEntry. + http://keepass.info/help/base/autotype.html + + + + + + + 1 if Two-Channel Auto-Type Obfuscation is enabled, otherwise 0. + http://keepass.info/help/v2/autotype_obfuscation.html + + + + + + + + + + + + + Custom window/sequence association + + + + + + + + + + + + + + + + Number of days before triggering an event e.g. suggesting a password change. + -1 means never. 0 means every time. + + + + + + + + + + + + A base64 encoded UUID. Each Group and Entry has a UUID. When added to History the same + UUID is maintained. + + + + + + + + A reference to the UUID e.g. a Group like the Recycle bin. If, for + example, the recycle bin is enabled but has not been used, then it won't have been + created and the value stored here is a 0 UUID. + + + + + + + + A reference to the custom icon UUID found in the meta/customIcons list. + + + + + + + + + Binary field for elements are centrally stored in this element. + The same Binary element may be referenced from many Entries. + + + + + + The ID attribute is referenced in the Ref attribute of + a corresponding Binary element of an Entry. The Compressed attribute specifies + whether the base64 encoded value is to be decompressed to recover the binary + data. + + + + + + + + + + + + + + + + + + + + Third party programs and plugins can put custom data here. + Unique element names should be used, e.g. "PluginName_ItemName". + + + + + + + Note that "LastModificationTime" is actually only allowed when this appears in "Meta" + and not when it appears in "Entry" or "Group" + + + + + + + + + + + + + + \ No newline at end of file diff --git a/KDBX.4.xsd b/XSD/KDBX.4.xsd similarity index 100% rename from KDBX.4.xsd rename to XSD/KDBX.4.xsd diff --git a/readme.md b/readme.md index 9fad5c8d..2ca84859 100644 --- a/readme.md +++ b/readme.md @@ -163,7 +163,8 @@ KeePass is in effect defined by the code that Dominik writes to create and maint [KDBX File Format Specification](https://keepass.info/help/kb/kdbx.html) describes the file format. There is also a discussion of the [differences between KDBX version 3.1 and version 4](https://keepass.info/help/kb/kdbx_4.html). Additionally, there is a discussion of the [enhancements in KDBX 4.1](https://keepass.info/help/kb/kdbx_4.1.html), as well -as a discussion of [Key Files](https://keepass.info/help/base/keys.html#keyfiles). +as a discussion of [Key Files](https://keepass.info/help/base/keys.html#keyfiles). While preparing release 2.2.3 I found [this XSD](https://keepass.info/help/download/KDBX_XML.xsd) at the +KeePass site. Massive credit also to the folks over at [KeePassXC](https://keepassxc.org/) who wrote some [documentation](https://github.com/keepassxreboot/keepassxc-specs) about their understanding of various format things. Also, this is a @@ -174,9 +175,8 @@ clarification and my own satisfaction I have written about my understanding of KeePass formats in the following locations: 1. The Javadoc header to [KdbxSerializer](http://javadoc.io/page/org.linguafranca.pwdb/KeePassJava2-kdbx/latest/org/linguafranca/pwdb/kdbx/stream_3_1/KdbxSerializer.html) describes KDBX stream formatting. -2. The XSD Schema [KDBX.4.xsd](KDBX.4.xsd) documents my understanding of the Keepass XML, and also my - lack of understanding, in parts. While preparing release 2.2.3 I found [this XSD](https://keepass.info/help/download/KDBX_XML.xsd) at the - KeePass site. I have not (so far) attempted to reconcile my documentation with it. +2. The XSD Schema [KDBX.4.1.xsd](./XSD/KDBX.4.1.xsd) documents my understanding of the Keepass XML, and also my + lack of understanding, in parts. 3. The following graphic illustrates KDBX 3.1 and 4 file formats: From 569a84bdb0458c9e743e6a1a3b65ff54712462f1 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 19:09:32 +0000 Subject: [PATCH 25/66] Updating doc cf release of 2.2.3 --- .travis.yml | 3 --- CHANGELOG.md | 2 +- bitbucket-pipelines.yml | 15 --------------- readme.md | 18 ++++-------------- 4 files changed, 5 insertions(+), 33 deletions(-) delete mode 100644 .travis.yml delete mode 100644 bitbucket-pipelines.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3d066ab9..00000000 --- a/.travis.yml +++ /dev/null @@ -1,3 +0,0 @@ -language: java -jdk: openjdk7 -script: mvn clean test diff --git a/CHANGELOG.md b/CHANGELOG.md index 16f977b7..0b0e8af5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,7 +11,7 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.co - remove DOM database - remove complicated generics on database classes -## 2024-11-22 [2.2.3-SNAPSHOT] +## 2025-01-05 [2.2.3] ### Added diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml deleted file mode 100644 index a8c1de44..00000000 --- a/bitbucket-pipelines.yml +++ /dev/null @@ -1,15 +0,0 @@ -# This is a sample build configuration for Java (Maven). -# Check our guides at https://confluence.atlassian.com/x/zd-5Mw for more examples. -# Only use spaces to indent your .yml configuration. -# ----- -# You can specify a custom docker image from Docker Hub as your build environment. -image: maven:3.5.2-jdk-7 - -pipelines: - #only on this branch - bitbucket-build: - - step: - caches: - - maven - script: # Modify the commands below to build your repository. - - mvn -B verify # -B batch mode makes Maven less verbose \ No newline at end of file diff --git a/readme.md b/readme.md index 2ca84859..1251f73a 100644 --- a/readme.md +++ b/readme.md @@ -3,11 +3,11 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.linguafranca.pwdb/KeePassJava2-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.linguafranca.pwdb/KeePassJava2-parent) [![javadoc](https://javadoc.io/badge2/org.linguafranca.pwdb/KeePassJava2/javadoc.svg)](https://javadoc.io/doc/org.linguafranca.pwdb/KeePassJava2) -![alt text](https://badgen.net/badge/Branch/master/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.2/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/master) +![alt text](https://badgen.net/badge/Branch/master/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.3/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/master) -![alt text](https://badgen.net/badge/Branch/develop/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.3-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) +![alt text](https://badgen.net/badge/Branch/develop/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.4-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) -![alt text](https://badgen.net/badge/Branch/v3/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.3-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/v3) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/fe9059ac4d384b929f452149b9246658)](https://app.codacy.com/gh/jorabin/KeePassJava2/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) +![alt text](https://badgen.net/badge/Branch/v3/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/3.0.0-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/v3) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/fe9059ac4d384b929f452149b9246658)](https://app.codacy.com/gh/jorabin/KeePassJava2/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) A Java 11 API for databases compatible with the renowned [KeePass](http://keepass.info) password @@ -189,16 +189,6 @@ is serializing and deserializing XML. Over time (KeePassJava2 was originally rel to Java and XML have been a bit mysterious. However, Jackson has now been chosen as the underlying framework for implementation of KeePassJava2. From 3.0.0 a single KDBX implementation is available. -There are several other database implementations which will be maintained for bug-fix purposes -only, with a view to being withdrawn, since they perform badly and/or depend on obsolete technology. - -- `SimpleXML` - no longer maintained, does not work with Java 17 and up -- `JAXB` - this causes problems with `javax` and `jakarta` namespaces, it's not worth maintaining - as it offers no compelling performance or other advantage -- `DOM` the was the original implementation and validates the fact that DOM based implementations - are slow. That said, if you want to load a database and then save it while maintaining whatever quirks - existed in the original database then this is the one. - Aside from dependencies on underlying frameworks, different implementations have varying characteristics, primarily speed. This is assessed by [this test](https://github.com/jorabin/KeePassJava2/blob/master/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java) in the module `examples`. @@ -287,7 +277,7 @@ Thanks to other contributors and raisers of issues. ## License -Copyright (c) 2024 Jo Rabin +Copyright (c) 2025 Jo Rabin Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From ea33a2eefb7c22e60c1b896bbcf3e8858e0b90fb Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 22:12:06 +0000 Subject: [PATCH 26/66] Update dependencies --- kdbx-database/pom.xml | 4 ++-- pom.xml | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index 00a25776..8ee2d613 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -36,12 +36,12 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - 2.17.1 + 2.18.2 com.fasterxml.woodstox woodstox-core - 7.0.0 + 7.1.0 org.linguafranca.pwdb diff --git a/pom.xml b/pom.xml index 424d45b6..a051962e 100644 --- a/pom.xml +++ b/pom.xml @@ -219,18 +219,18 @@ org.jetbrains annotations - 24.1.0 + 26.0.1 com.google.guava guava - 32.1.2-android + 33.4.0-jre org.bouncycastle bcpkix-jdk18on - 1.78.1 + 1.79 org.slf4j @@ -242,7 +242,7 @@ ch.qos.logback logback-classic - 1.3.15 + 1.5.15 commons-codec From a9a8de59e8932e8d88fb6245300a7ba0809df6dc Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sun, 5 Jan 2025 22:23:07 +0000 Subject: [PATCH 27/66] update maven plugins --- pom.xml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index a051962e..3ff2027b 100644 --- a/pom.xml +++ b/pom.xml @@ -68,16 +68,16 @@ 11 3.13.0 - 3.5.0 + 3.5.2 1.7.0 3.3.1 - 3.10.0 - 3.2.6 + 3.11.2 + 3.2.7 3.1.1 3.4.2 - 3.8.0 + 3.8.1 3.4.0 - 2.17.1 + 2.18.0 3.5.0 false From 1da9f70342ead0781ddba2268b07501ef295dc54 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Thu, 6 Mar 2025 12:41:52 +0000 Subject: [PATCH 28/66] create KdbxCredentials to replace KdbxCreds --- .../pwdb/format/KdbxCredentials.java | 90 +++++++------------ .../linguafranca/pwdb/format/KdbxCreds.java | 50 ++--------- 2 files changed, 43 insertions(+), 97 deletions(-) diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java index a21aefb2..87877c64 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java @@ -16,83 +16,61 @@ package org.linguafranca.pwdb.format; +import org.jetbrains.annotations.NotNull; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.security.Encryption; -import org.jetbrains.annotations.NotNull; import java.io.InputStream; import java.security.MessageDigest; /** - * The class holds subclasses of {@link Credentials} appropriate to KDBX files. + * The class implements {@link Credentials} for KDBX files. * * @author jo - * @deprecated use KdbxCreds instead */ -@Deprecated -@SuppressWarnings("deprecation") -public interface KdbxCredentials extends Credentials { +public class KdbxCredentials implements Credentials { + + private final byte[] key; /** - * Class for KDBX key file with password credentials + * Constructor for password with KDBX Keyfile + * @param password Master Password (new byte[0] if empty, not none) + * @param inputStream inputStream of the keyfile */ - class KeyFile implements KdbxCredentials { - - private final byte[] key; - - /** - * Constructor for password with KDBX Keyfile - * @param password Master Password (new byte[0] if empty, not none) - * @param inputStream inputstream of the keyfile - */ - public KeyFile(@NotNull byte[] password, @NotNull InputStream inputStream) { - MessageDigest md = Encryption.getSha256MessageDigestInstance(); - byte[] pwKey = md.digest(password); - md.update(pwKey); - - byte[] keyFileData = KdbxKeyFile.load(inputStream); - if (keyFileData == null) { - throw new IllegalStateException("Could not read key file"); - } - this.key = md.digest(keyFileData); - } - - /** - * Constructor for KDBX Keyfile with no password - * @param inputStream inputstream of the keyfile - */ - public KeyFile(@NotNull InputStream inputStream) { - MessageDigest md = Encryption.getSha256MessageDigestInstance(); + public KdbxCredentials(byte @NotNull [] password, @NotNull InputStream inputStream) { + MessageDigest md = Encryption.getSha256MessageDigestInstance(); + byte[] pwKey = md.digest(password); + md.update(pwKey); - byte[] keyFileData = KdbxKeyFile.load(inputStream); - if (keyFileData == null) { - throw new IllegalStateException("Could not read key file"); - } - this.key = md.digest(keyFileData); - } - - @Override - public byte[] getKey() { - return key; + byte[] keyFileData = KdbxKeyFile.load(inputStream); + if (keyFileData == null) { + throw new IllegalStateException("Could not read key file"); } + this.key = md.digest(keyFileData); } /** - * Class for KDBX password only credentials + * Constructor for KDBX Keyfile with no password + * @param inputStream inputStream of the keyfile */ - class Password implements KdbxCredentials { + public KdbxCredentials(@NotNull InputStream inputStream) { + MessageDigest md = Encryption.getSha256MessageDigestInstance(); + byte[] keyFileData = KdbxKeyFile.load(inputStream); + if (keyFileData == null) { + throw new IllegalStateException("Could not read key file"); + } + this.key = md.digest(keyFileData); + } - private final byte[] key; - public Password(@NotNull byte[] password) { - MessageDigest md = Encryption.getSha256MessageDigestInstance(); - byte[] digest = md.digest(password); - key = md.digest(digest); - } + public KdbxCredentials(byte @NotNull [] password) { + MessageDigest md = Encryption.getSha256MessageDigestInstance(); + byte[] digest = md.digest(password); + key = md.digest(digest); + } - @Override - public byte[] getKey() { - return key; - } + @Override + public byte[] getKey() { + return key; } } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java index eac69171..1ebeb464 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java @@ -24,53 +24,21 @@ import java.security.MessageDigest; /** - * The class implements {@link Credentials} for KDBX files. + * Convenience wrapper to convert to KdbxCredentials - for helping with code migration to version 3. * - * @author jo + * @deprecated use {@link KdbxCredentials} instead */ -public class KdbxCreds implements Credentials { - - private final byte[] key; - - /** - * Constructor for password with KDBX Keyfile - * @param password Master Password (new byte[0] if empty, not none) - * @param inputStream inputstream of the keyfile - */ - public KdbxCreds(@NotNull byte[] password, @NotNull InputStream inputStream) { - MessageDigest md = Encryption.getSha256MessageDigestInstance(); - byte[] pwKey = md.digest(password); - md.update(pwKey); - - byte[] keyFileData = KdbxKeyFile.load(inputStream); - if (keyFileData == null) { - throw new IllegalStateException("Could not read key file"); - } - this.key = md.digest(keyFileData); +@Deprecated +public class KdbxCreds extends KdbxCredentials { + public KdbxCreds(byte @NotNull [] password, @NotNull InputStream inputStream) { + super(password, inputStream); } - /** - * Constructor for KDBX Keyfile with no password - * @param inputStream inputstream of the keyfile - */ public KdbxCreds(@NotNull InputStream inputStream) { - MessageDigest md = Encryption.getSha256MessageDigestInstance(); - byte[] keyFileData = KdbxKeyFile.load(inputStream); - if (keyFileData == null) { - throw new IllegalStateException("Could not read key file"); - } - this.key = md.digest(keyFileData); - } - - - public KdbxCreds(@NotNull byte[] password) { - MessageDigest md = Encryption.getSha256MessageDigestInstance(); - byte[] digest = md.digest(password); - key = md.digest(digest); + super(inputStream); } - @Override - public byte[] getKey() { - return key; + public KdbxCreds(byte @NotNull [] password) { + super(password); } } From eff33e7b2915e7dc4082fa6ddb40cd85f1c4bc90 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 7 Mar 2025 16:00:13 +0000 Subject: [PATCH 29/66] Doc --- PropertyValueProtection.md | 7 +++-- readme.md | 58 ++++++++++++++++++-------------------- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/PropertyValueProtection.md b/PropertyValueProtection.md index 39e130ef..f0e12a05 100644 --- a/PropertyValueProtection.md +++ b/PropertyValueProtection.md @@ -71,9 +71,10 @@ the saved passwords (as per the requirements), hashing does not meet the objecti What key should be used for this process? If the key is stored in RAM, we are essentially creating the same security vulnerability we are trying to avoid. -While storing data in an encrypted form may seem appealing, keeping it encrypted in the KDBX InnerStream is impractical. -This approach requires that the encrypted property values appear in the same order for both encryption and decryption, which makes it cumbersome. -Encrypting and decrypting all protected fields each time we need to access or manipulate just one of them would add significant overhead. +While storing data in an encrypted form may seem appealing, keeping it encrypted in the KDBX InnerStream +is impractical. This approach requires that the encrypted property values appear in the same order for both +encryption and decryption, which makes it cumbersome. Encrypting and decrypting all protected fields each +time we need to access or manipulate just one of them would add significant overhead. If the data is to be encrypted, then we need to find some way of storing the encryption key that is not open to simple inspection. diff --git a/readme.md b/readme.md index 1251f73a..ce22349a 100644 --- a/readme.md +++ b/readme.md @@ -3,27 +3,28 @@ [![Maven Central](https://maven-badges.herokuapp.com/maven-central/org.linguafranca.pwdb/KeePassJava2-parent/badge.svg)](https://maven-badges.herokuapp.com/maven-central/org.linguafranca.pwdb/KeePassJava2-parent) [![javadoc](https://javadoc.io/badge2/org.linguafranca.pwdb/KeePassJava2/javadoc.svg)](https://javadoc.io/doc/org.linguafranca.pwdb/KeePassJava2) -![alt text](https://badgen.net/badge/Branch/master/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.3/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/master) +![alt text](https://badgen.net/badge/Branch/master/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.4/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/master.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/master) -![alt text](https://badgen.net/badge/Branch/develop/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.4-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) +![alt text](https://badgen.net/badge/Branch/develop/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/2.2.5-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/develop) ![alt text](https://badgen.net/badge/Branch/v3/yellow?icon=github) ![alt text](https://badgen.net/badge/Build/3.0.0-SNAPSHOT/blue?icon=github) [![CircleCI](https://dl.circleci.com/status-badge/img/gh/jorabin/KeePassJava2/tree/develop.svg?style=shield)](https://dl.circleci.com/status-badge/redirect/gh/jorabin/KeePassJava2/tree/v3) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/fe9059ac4d384b929f452149b9246658)](https://app.codacy.com/gh/jorabin/KeePassJava2/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) -A Java 11 API for databases compatible with the renowned [KeePass](http://keepass.info) password +Java 11 API (from version 3.0.0 upwards) for password databases compatible with the renowned [KeePass](http://keepass.info) password safe for Windows. This is a "headless" implementation - if you want something with a UI then [KeePassXC](https://keepassxc.org/) and [KeePassDX](https://www.keepassdx.com/) could be just the things for you. Features to date: -- Read and write KeePass 2.x format (KDBX file formats V3 and V4) +- Read and write KeePass 2.x format (KDBX file formats V3.1, V4 and V4.1) - Keepass 2.x Password and Keyfile Credentials +- Pluggable memory storage and protection strategy - Read KeePass 1.x format (KDB format, Rijndael only) - *No* requirement for JCE Policy Files - Android compatible - Interfaces for Database, Group and Entry allow compatible addition of other formats -- Pluggable memory storage and protection strategy + It is licensed under the Apache 2 License and is currently usable. @@ -41,15 +42,18 @@ It is licensed under the Apache 2 License and is currently usable. ## Current Status -This version is 3.0.0-SNAPSHOT and is not backwards compatible with 2.3 and earlier versions. +This version is 3.0.0-SNAPSHOT. It is the intention that maintenance will cease +on version 2 at some point during 2025 once this version is released. +Upgrade to V3 requires minor changes to V2 code. -The current released code is version 2.2.3 - released to Maven January 2025. This is on the main branch. See [Build from Source](#build-from-source) +The current released version is version 2.2.4 - released to Maven March 2025. +This is on the main branch. See [Build from Source](#build-from-source) Key updates relative to 2.x - Java 11 +- Pluggable (protected) data storage model - File format version 4 support - with Argon2 - Removal of SimpleXML, JAXB and JAXB database implementations -- Implemented pluggable (protected) data storage model - Removed generics on database classes - Refactor modules and packages - Updated keyfile support @@ -61,25 +65,12 @@ See the [changelog](CHANGELOG.md) for more details. ### Release -The POM for the last release (2.2.3), Java 8 compatible, is - - org.linguafranca.pwdb - KeePassJava2-jackson - 2.2.3 - -at Maven Central. This provides access to the Jackson based implementation, -which is now the recommended implementation. There is also a composite POM (deprecated) that -provides access to all implementations (see [below](#database-implementations) for discussion). - - org.linguafranca.pwdb - KeePassJava2 - 2.2.3 - -at Maven Central. Note that the artifactId has become Camel Case from release 2.1.x onwards. +For the POM for the last release, see the [main branch](https://github.com/jorabin/KeePassJava2/tree/master). +As noted, Version 2.x will no longer be maintained once this version is released. ### Snapshot -Snapshot builds are erratically available at [Sonatype](https://oss.sonatype.org/content/repositories/snapshots/org/linguafranca/pwdb/) e.g.: +Snapshot builds, such as this version 3.0.0, are available at [Sonatype](https://oss.sonatype.org/content/repositories/snapshots/org/linguafranca/pwdb/) e.g.: org.linguafranca.pwdb KeePassJava2 @@ -100,18 +91,18 @@ with appropriate `` entry, like: - There are also separate POMs for the various modules. The module structure is illustrated below + The module structure is illustrated below under [Build from Source](#build-from-source). ## Java Version -From release 2.2 it requires Java 1.8. Earlier versions require Java 1.7. +Versions 3.0.0 onwards require Java 11. From version 2.2 Java 1.8 is required. Earlier versions require Java 1.7. ## Quick Start Create credentials and an input stream for the password vault in question: - KdbxCreds creds = new KdbxCreds("123".getBytes()); + KdbxCredentials credentials = new KdbxCredentials("123".getBytes()); InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test1.kdbx"); then load the database: @@ -126,15 +117,20 @@ and one to support the KeePass V2 KDB format (`KdbDatabase`). There are numerous well-understood problems with storing passwords as Strings in Java. See [this discussion](./PropertyValueProtection.md) about the -KeePassJava2 approach to storing passwords, which is implemented in this release. +KeePassJava2 approach to storing passwords. ### Discussion Password databases are modelled as a three layer abstraction. -A *Database* is a collection of records whose physical representation needs only to be capable of rendering as a stream. *Entries* hold the information of value in the database and *Groups* allow the structuring of entries into collections, just like a folder structure. +A *Database* is a collection of records whose physical representation needs only to be capable +of rendering as a stream. *Entries* hold the information of value in the database and *Groups* +allow the structuring of entries into collections, just like a folder structure. -The Database has a root group and by following subgroups of the root group the tree structure of the database can be navigated. Entries belong to groups. Entries can be moved between groups and groups can also be moved between groups. However, entries and groups created in one database cannot be moved to another database without being converted: +The Database has a root group and by following subgroups of the root group the tree structure of +the database can be navigated. Entries belong to groups. Entries can be moved between groups and groups +can also be moved between groups. However, entries and groups created in one database cannot be moved to +another database without being converted: database.newEntry(entryToCopy); database.newGroup(groupToCopy); @@ -145,7 +141,7 @@ The class Javadoc on Interface classes [Entry](http://javadoc.io/page/org.linguafranca.pwdb/database/latest/org/linguafranca/pwdb/Entry.html) describe how to use the methods of those classes to create and modify entries. These classes provide the basis of all implementations of the various database formats, -initially KDB, KDBX 3.1 and KDBX 4 (KeePass 2) file formats, subsequently, potentially, others. +KDBX 3.1, 4 and 4.1 (KeePass 2) as well as KDB (KeePass 1), file formats. The class [QuickStart.java](example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java) provides some illustrations of operations using the Database, Group and Entry interfaces. From 13566e07ef0a9afd8a663664bce5a21cbbaf1274 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 7 Mar 2025 16:02:48 +0000 Subject: [PATCH 30/66] Jackson Serialization of Empty Elements cf #76 and #78 --- .../java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java | 3 +++ .../pwdb/kdbx/jackson/KdbxSerializableDatabase.java | 2 ++ kdbx-io/src/main/resources/base.kdbx.xml | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index 475b43f1..c0a44121 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -194,6 +194,9 @@ public boolean shouldProtect(String s) { } public List getBinaries() { + if (keePassFile.meta.binaries == null) { + keePassFile.createBinaries(); + } return keePassFile.meta.binaries; } diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index 016fb200..c8259257 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -17,6 +17,7 @@ import com.ctc.wstx.api.WstxInputProperties; import com.ctc.wstx.api.WstxOutputProperties; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.SerializationFeature; @@ -104,6 +105,7 @@ public void save(OutputStream outputStream) { mapper.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); // set the serializer to Woodstox System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory"); diff --git a/kdbx-io/src/main/resources/base.kdbx.xml b/kdbx-io/src/main/resources/base.kdbx.xml index 5a791ae4..1958b946 100644 --- a/kdbx-io/src/main/resources/base.kdbx.xml +++ b/kdbx-io/src/main/resources/base.kdbx.xml @@ -16,7 +16,7 @@ - Keepass-Java-2 + KeePassJava2 New Database ${creationDate} From c00dc819f2f5d9f8b59597356dc4979eeafdabbb Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 7 Mar 2025 16:49:45 +0000 Subject: [PATCH 31/66] Update to use KdbxCredentials in place of KdbxCreds --- .../java/org/linguafranca/pwdb/Database.java | 2 +- .../linguafranca/pwdb/kdbx/QuickStart.java | 48 ++++++++++--------- .../pwdb/kdbx/SimpleQuickStartTest.java | 18 +------ .../pwdb/kdbx/jackson/KdbxDatabase.java | 20 ++++---- .../pwdb/format/KdbxCredentials.java | 4 ++ 5 files changed, 42 insertions(+), 50 deletions(-) diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index 0d95d0c7..1d2cf0c8 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -237,7 +237,7 @@ public interface Database { void save(Credentials credentials, OutputStream outputStream) throws IOException; /** - * Save the database to a stream + * Save the database to a stream and closes the stream */ void save(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) throws IOException; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 2b77c8c0..b342f398 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.*; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdb.KdbCredentials; @@ -26,6 +26,8 @@ import org.linguafranca.pwdb.security.Encryption; import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -38,13 +40,9 @@ @SuppressWarnings("WeakerAccess") public abstract class QuickStart { - public static final String TEST_OUTPUT_DIR = "testOutput"; + public static final String TEST_OUTPUT_DIR = "testOutput/"; static PrintStream printStream = getTestPrintStream(); - - public abstract Database getDatabase(); - public abstract Database loadDatabase(Credentials creds, InputStream inputStream); - /** * Load KDBX */ @@ -52,9 +50,9 @@ public void loadKdbx() throws IOException { // get an input stream try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx")) { // password credentials - Credentials credentials = new KdbxCreds("123".getBytes()); + Credentials credentials = new KdbxCredentials("123".getBytes()); // Jaxb implementation seems a lot faster than the DOM implementation - Database database = loadDatabase(credentials, inputStream); + Database database = KdbxDatabase.load(credentials, inputStream); // visit all groups and entries and list them to console database.visit(new Visitor.Print(printStream)); } @@ -70,7 +68,7 @@ private Entry entryFactory(Database database, String s, int e) { public void saveKdbx() throws IOException { // create an empty database - Database database = getDatabase(); + Database database = new KdbxDatabase(); // add some groups and entries for (int g = 0; g < 5; g++) { @@ -82,8 +80,8 @@ public void saveKdbx() throws IOException { } // save to a file with password "123" - try (FileOutputStream outputStream = new FileOutputStream(TEST_OUTPUT_DIR + "/test.kdbx")) { - database.save(new KdbxCreds("123".getBytes()), outputStream); + try (FileOutputStream outputStream = new FileOutputStream(TEST_OUTPUT_DIR + "test.kdbx")) { + database.save(new KdbxCredentials("123".getBytes()), outputStream); } } @@ -124,31 +122,31 @@ public void loadKdb() throws IOException { // visit all groups and entries and list them to console // create a KDBX (database - Database kdbxDatabase = getDatabase(); + Database kdbxDatabase = new KdbxDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDB Database to KDBX Database"); // deep copy from group (not including source group, KDB database has simulated root) kdbxDatabase.getRootGroup().copy(database.getRootGroup()); // save it - try (FileOutputStream f = new FileOutputStream(TEST_OUTPUT_DIR + "/migration.kdbx")) { - kdbxDatabase.save(new KdbxCreds("123".getBytes()), f); + try (FileOutputStream f = new FileOutputStream(TEST_OUTPUT_DIR + "migration.kdbx")) { + kdbxDatabase.save(new KdbxCredentials("123".getBytes()), f); } } /** * Load KDBX V3 save as KDBX V4 */ - public void loadKdbx3SaveKdbx4(String resourceName, byte[] password, OutputStream v4OutputStream) throws IOException { + public void loadKdbx3SaveKdbx4(String v3ResourceName, byte[] password, Path v4Filename) throws IOException { KdbxDatabase database; // password credentials - KdbxCreds credentials = new KdbxCreds(password); - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { + KdbxCredentials credentials = new KdbxCredentials(password); + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(v3ResourceName)) { // load KdbDatabase database = KdbxDatabase.load(credentials, inputStream); } // create a KDBX (database - Database kdbxDatabase = getDatabase(); + Database kdbxDatabase = new KdbxDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDBX 3 Database to KDBX 4 Database"); // deep copy from group (not including source group, KDB database has simulated root) @@ -163,24 +161,26 @@ public void loadKdbx3SaveKdbx4(String resourceName, byte[] password, OutputStrea kdbxHeader.setProtectedStreamAlgorithm(Encryption.ProtectedStreamAlgorithm.CHA_CHA_20); // save it with format options - kdbxDatabase.save(formatV4, credentials, v4OutputStream); + try (OutputStream v4OutputStream = Files.newOutputStream(v4Filename)) { + kdbxDatabase.save(formatV4, credentials, v4OutputStream); + } } /** * Load KDBX V3 save as KDBX V4 */ - public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, OutputStream v3OutputStream) throws IOException { + public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3Filename) throws IOException { KdbxDatabase database; // password credentials - KdbxCreds credentials = new KdbxCreds(password); + KdbxCredentials credentials = new KdbxCredentials(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase database = KdbxDatabase.load(credentials, inputStream); } // create a KDBX (database) - Database kdbxDatabase = getDatabase(); + Database kdbxDatabase = new KdbxDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDBX 4 Database to KDBX 3 Database"); // deep copy from group (not including source group @@ -191,6 +191,8 @@ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, OutputStrea KdbxStreamFormat formatV3 = new KdbxStreamFormat(kdbxHeader); // save it with format options - kdbxDatabase.save(formatV3, credentials, v3OutputStream); + try (OutputStream v3OutputStream = Files.newOutputStream(v3Filename)) { + kdbxDatabase.save(formatV3, credentials, v3OutputStream); + } } } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index aceedbf7..b4dc0210 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -40,20 +40,6 @@ public class SimpleQuickStartTest extends QuickStart { - @Override - public KdbxDatabase getDatabase() { - return new KdbxDatabase(); - } - - @Override - public KdbxDatabase loadDatabase(Credentials credentials, InputStream inputStream){ - try { - return KdbxDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - @BeforeClass public static void ensureOutputDir() throws IOException { Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); @@ -78,7 +64,7 @@ public void loadKdbSaveVernacular() throws IOException { public void loadSave() throws IOException { Path path = Paths.get("testOutput/CHACHA-AES-CHACHA.kdbx"); - loadKdbx3SaveKdbx4("test123.kdbx","123".getBytes(), Files.newOutputStream(path)); + loadKdbx3SaveKdbx4("test123.kdbx","123".getBytes(), path); // load newly created V4 database KdbxDatabase db = KdbxDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); @@ -92,7 +78,7 @@ public void loadSave() throws IOException { public void loadSave2() throws IOException { Path path = Paths.get("testOutput/CHACHA-AES-CHACHA.kdbx"); - loadKdbx4SaveKdbx3("V4-ChaCha20-Argon2-Attachment.kdbx","123".getBytes(), Files.newOutputStream(path)); + loadKdbx4SaveKdbx3("V4-ChaCha20-Argon2-Attachment.kdbx","123".getBytes(), path); // load newly created V4 database KdbxDatabase db = KdbxDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index 475b43f1..d4a5c928 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -85,9 +85,9 @@ public static KdbxDatabase load(Credentials credentials, InputStream inputStream /** * Save the database with the same stream format that it was loaded with, or V4 * default if none - * + * * @param credentials credentials to use - * @param outputStream where to write to + * @param outputStream where to write to - closes stream */ @Override public void save(Credentials credentials, OutputStream outputStream) throws IOException { @@ -99,19 +99,19 @@ public void save(Credentials credentials, OutputStream outputStream) throws IOEx /** * Save the database with a choice of stream format - * + * * @param streamFormat the format to use * @param credentials credentials to use - * @param outputStream where to write to + * @param outputStream where to write to - call closes output stream */ @Override public void save(StreamFormat streamFormat, Credentials credentials, - OutputStream outputStream) throws IOException { - keePassFile.meta.generator = "KeePassJava2-Jackson"; - KdbxSerializableDatabase kdbxSerializableDatabase = new KdbxSerializableDatabase(this.keePassFile); - kdbxSerializableDatabase.setPropertyValueStrategy(this.getPropertyValueStrategy()); - streamFormat.save(kdbxSerializableDatabase, credentials, outputStream); - setDirty(false); + OutputStream outputStream) throws IOException { + keePassFile.meta.generator = "KeePassJava2-V3-Jackson"; + KdbxSerializableDatabase kdbxSerializableDatabase = new KdbxSerializableDatabase(this.keePassFile); + kdbxSerializableDatabase.setPropertyValueStrategy(this.getPropertyValueStrategy()); + streamFormat.save(kdbxSerializableDatabase, credentials, outputStream); + setDirty(false); } @Override diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java index 87877c64..ea3551fd 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java @@ -63,6 +63,10 @@ public KdbxCredentials(@NotNull InputStream inputStream) { } + /** + * Constructor for password only + * @param password Master Password for database (new byte[0] if empty, not none) + */ public KdbxCredentials(byte @NotNull [] password) { MessageDigest md = Encryption.getSha256MessageDigestInstance(); byte[] digest = md.digest(password); From 0a8e60f5e04992e96aad4681e6144e364c080ef1 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 7 Mar 2025 16:51:56 +0000 Subject: [PATCH 32/66] Update copyright --- DatabaseStructure.svg | 17 +++++++++++++++++ KdbxDiagram.svg | 17 +++++++++++++++++ ModuleStructure.svg | 17 +++++++++++++++++ XSD/KDBX.3.1.xsd | 5 +++-- XSD/KDBX.4.1.reichl.xsd | 17 +++++++++++++++++ XSD/KDBX.4.1.xsd | 5 +++-- XSD/KDBX.4.xsd | 5 +++-- all/pom.xml | 5 +++-- .../java/org/linguafranca/all/IgnoreMe.java | 17 +++++++++++++++++ .../java/org/linguafranca/all/package-info.java | 17 +++++++++++++++++ database/pom.xml | 17 +++++++++++++++++ .../java/org/linguafranca/pwdb/Credentials.java | 5 +++-- .../java/org/linguafranca/pwdb/Database.java | 5 +++-- .../main/java/org/linguafranca/pwdb/Entry.java | 5 +++-- .../main/java/org/linguafranca/pwdb/Group.java | 5 +++-- .../main/java/org/linguafranca/pwdb/Icon.java | 5 +++-- .../org/linguafranca/pwdb/PropertyValue.java | 17 +++++++++++++++++ .../linguafranca/pwdb/SerializableDatabase.java | 5 +++-- .../linguafranca/pwdb/StreamConfiguration.java | 17 +++++++++++++++++ .../org/linguafranca/pwdb/StreamFormat.java | 5 +++-- .../java/org/linguafranca/pwdb/Visitor.java | 5 +++-- .../pwdb/base/AbstractDatabase.java | 5 +++-- .../linguafranca/pwdb/base/AbstractEntry.java | 5 +++-- .../linguafranca/pwdb/base/AbstractGroup.java | 5 +++-- .../pwdb/protect/ProtectedDatabase.java | 17 +++++++++++++++++ .../org/linguafranca/pwdb/security/Aes.java | 17 +++++++++++++++++ .../org/linguafranca/pwdb/security/Argon2.java | 17 +++++++++++++++++ .../org/linguafranca/pwdb/security/ChaCha.java | 17 +++++++++++++++++ .../pwdb/security/CipherAlgorithm.java | 17 +++++++++++++++++ .../linguafranca/pwdb/security/Encryption.java | 5 +++-- .../pwdb/security/KeyDerivationFunction.java | 17 +++++++++++++++++ .../pwdb/security/StreamEncryptor.java | 5 +++-- .../pwdb/security/VariantDictionary.java | 17 +++++++++++++++++ .../pwdb/util/PropertyValueUtil.java | 17 +++++++++++++++++ .../linguafranca/pwdb/PropertyValueTest.java | 17 +++++++++++++++++ example/pom.xml | 5 +++-- .../example/SecureCharBufferPropertyValue.java | 17 +++++++++++++++++ .../org/linguafranca/pwdb/kdbx/ChooseFile.java | 17 +++++++++++++++++ .../java/org/linguafranca/pwdb/kdbx/Fluent.java | 17 +++++++++++++++++ .../linguafranca/pwdb/kdbx/OpenDbExample.java | 17 +++++++++++++++++ .../org/linguafranca/pwdb/kdbx/QuickStart.java | 5 +++-- .../org/linguafranca/pwdb/kdbx/SaxParse.java | 5 +++-- .../java/org/linguafranca/pwdb/kdbx/Util.java | 17 +++++++++++++++++ .../SecureCharBufferPropertyValueTest.java | 17 +++++++++++++++++ .../linguafranca/pwdb/kdbx/SaxParseTest.java | 5 +++-- .../pwdb/kdbx/SimpleQuickStartTest.java | 5 +++-- .../org/linguafranca/pwdb/kdbx/UtilTest.java | 17 +++++++++++++++++ .../pwdb/kdbx/validation/Issue27Test.java | 17 +++++++++++++++++ .../pwdb/kdbx/validation/Issue33Test.java | 17 +++++++++++++++++ .../pwdb/kdbx/validation/Issue38Test.java | 17 +++++++++++++++++ .../pwdb/kdbx/validation/Issue70Test.java | 17 +++++++++++++++++ kdb/pom.xml | 17 +++++++++++++++++ .../linguafranca/pwdb/kdb/KdbCredentials.java | 5 +++-- .../org/linguafranca/pwdb/kdb/KdbDatabase.java | 5 +++-- .../org/linguafranca/pwdb/kdb/KdbEntry.java | 5 +++-- .../org/linguafranca/pwdb/kdb/KdbGroup.java | 5 +++-- .../org/linguafranca/pwdb/kdb/KdbHeader.java | 5 +++-- .../java/org/linguafranca/pwdb/kdb/KdbIcon.java | 5 +++-- .../linguafranca/pwdb/kdb/KdbSerializer.java | 5 +++-- .../pwdb/kdb/KdbDatabaseLoaderTest.java | 5 +++-- .../linguafranca/pwdb/kdb/KdbDatabaseTest.java | 5 +++-- .../pwdb/kdb/KdbSerializerTest.java | 5 +++-- .../org/linguafranca/pwdb/kdb/KeyFileTest.java | 17 +++++++++++++++++ kdbx-database/pom.xml | 5 +++-- .../pwdb/kdbx/jackson/KdbxDatabase.java | 5 +++-- .../pwdb/kdbx/jackson/KdbxEntry.java | 5 +++-- .../pwdb/kdbx/jackson/KdbxGroup.java | 5 +++-- .../pwdb/kdbx/jackson/KdbxHistory.java | 5 +++-- .../pwdb/kdbx/jackson/KdbxIcon.java | 5 +++-- .../kdbx/jackson/KdbxSerializableDatabase.java | 5 +++-- .../converter/Base64ToByteConverter.java | 5 +++-- .../converter/Base64ToUUIDConverter.java | 5 +++-- .../converter/BooleanToStringConverter.java | 5 +++-- .../converter/ByteToBase64Converter.java | 5 +++-- .../converter/DateToStringConverter.java | 5 +++-- .../converter/StringToBooleanConverter.java | 5 +++-- .../converter/StringToDateConverter.java | 5 +++-- .../converter/UUIDToBase64Converter.java | 5 +++-- .../jackson/converter/ValueDeserializer.java | 5 +++-- .../kdbx/jackson/converter/ValueSerializer.java | 5 +++-- .../pwdb/kdbx/jackson/model/EntryClasses.java | 5 +++-- .../pwdb/kdbx/jackson/model/KeePassFile.java | 5 +++-- .../pwdb/kdbx/jackson/model/Times.java | 5 +++-- .../pwdb/JacksonBinaryPropertyV3Test.java | 5 +++-- .../pwdb/JacksonBinaryPropertyV4Test.java | 5 +++-- .../pwdb/JacksonPropertyValueTest.java | 5 +++-- .../pwdb/JacksonRecycleBinTest.java | 5 +++-- .../pwdb/JacksonSaveAndReloadTest.java | 5 +++-- .../linguafranca/pwdb/KdbxDatabaseLoadTest.java | 5 +++-- .../pwdb/KdbxDatabaseLoaderTest.java | 5 +++-- .../org/linguafranca/pwdb/KdbxDatabaseTest.java | 5 +++-- kdbx-io/pom.xml | 5 +++-- .../org/linguafranca/pwdb/format/Helpers.java | 5 +++-- .../pwdb/format/KdbxCredentials.java | 5 +++-- .../org/linguafranca/pwdb/format/KdbxCreds.java | 5 +++-- .../linguafranca/pwdb/format/KdbxHeader.java | 5 +++-- .../linguafranca/pwdb/format/KdbxKeyFile.java | 5 +++-- .../pwdb/format/KdbxSerializer.java | 5 +++-- .../pwdb/format/KdbxStreamFormat.java | 5 +++-- .../pwdb/hashedblock/CollectingInputStream.java | 17 +++++++++++++++++ .../hashedblock/CollectingOutputStream.java | 17 +++++++++++++++++ .../hashedblock/HashedBlockInputStream.java | 5 +++-- .../hashedblock/HashedBlockOutputStream.java | 5 +++-- .../pwdb/hashedblock/HmacBlockInputStream.java | 17 +++++++++++++++++ .../pwdb/hashedblock/HmacBlockOutputStream.java | 17 +++++++++++++++++ kdbx-io/src/main/resources/base.kdbx.xml | 5 +++-- .../pwdb/hashedblock/HashBlockTest.java | 5 +++-- .../org/linguafranca/pwdb/kdbx/HelperTest.java | 5 +++-- .../org/linguafranca/pwdb/kdbx/HelpersTest.java | 17 +++++++++++++++++ .../linguafranca/pwdb/kdbx/KdbxHeaderTest.java | 17 +++++++++++++++++ .../linguafranca/pwdb/kdbx/KdbxKeyFileTest.java | 5 +++-- .../pwdb/kdbx/KdbxSerializerTest.java | 5 +++-- .../pwdb/security/StreamEncryptionTest.java | 5 +++-- logback.xml | 17 +++++++++++++++++ pom.xml | 5 +++-- test/pom.xml | 17 +++++++++++++++++ .../pwdb/checks/BasicDatabaseChecks.java | 17 +++++++++++++++++ .../pwdb/checks/BinaryPropertyChecks.java | 17 +++++++++++++++++ .../pwdb/checks/DatabaseLoaderChecks.java | 17 +++++++++++++++++ .../pwdb/checks/PropertyValueChecks.java | 17 +++++++++++++++++ .../pwdb/checks/RecycleBinChecks.java | 17 +++++++++++++++++ .../pwdb/checks/SaveAndReloadChecks.java | 17 +++++++++++++++++ test/src/test/resources/Attachment.xml | 17 +++++++++++++++++ test/src/test/resources/ExampleDatabase.xml | 17 +++++++++++++++++ test/src/test/resources/kdbxSample.xml | 5 +++-- test/src/test/resources/xml/V4-AES-AES.xml | 17 +++++++++++++++++ test/src/test/resources/xml/V4-AES-Argon2.xml | 17 +++++++++++++++++ test/src/test/resources/xml/V4-ChaCha20-AES.xml | 17 +++++++++++++++++ .../xml/V4-ChaCha20-Argon2-Attachment.xml | 17 +++++++++++++++++ util/pom.xml | 17 +++++++++++++++++ .../java/org/linguafranca/util/HexViewer.java | 17 +++++++++++++++++ .../org/linguafranca/util/MavenRunListener.java | 17 +++++++++++++++++ .../java/org/linguafranca/util/TestUtil.java | 17 +++++++++++++++++ 133 files changed, 1169 insertions(+), 156 deletions(-) diff --git a/DatabaseStructure.svg b/DatabaseStructure.svg index 0694e7aa..6e7d6b57 100644 --- a/DatabaseStructure.svg +++ b/DatabaseStructure.svg @@ -1,4 +1,21 @@ + +
Save/Load
Save/Load
SerializableDatabase
(Jackson, DOM, JAXB, Simple)
SerializableDatabase...
Encrypt/Decrypt
Encrypt/Decrypt
StreamFormat
(Kdbx, None)
StreamFormat...
Read/Write Values
Read/Write Values
Save/Load
Save/Load
Database Implementation
(Memory Model, DOM etc.)
Database Implementation...
Save/Load
Save/Load
User Read/Write
User Read/Write
Database Facade
(Jackson, DOM Wrapper, JAXB, SImple)
Database Facade...
Text is not SVG - cannot display
\ No newline at end of file diff --git a/KdbxDiagram.svg b/KdbxDiagram.svg index fb278b1e..6aaefa8e 100644 --- a/KdbxDiagram.svg +++ b/KdbxDiagram.svg @@ -1,4 +1,21 @@ + +
Magic Number
Magic Number
Version
Version
Header
Header
Header
Header
Header
Header
Header 0
Header 0
...
...
Value of Header 9
Value of Header 9
0
0
8
8
4
4
Type
Type
Length
Length
Value
Value
Encrypted
Encryp...
Unencrypted
Unencr...
Sequence
Sequence
MD5 Hash
MD5 Hash
Length
Length
KDBX 3.1 File
KDBX 3.1 File
Header Format
Header Format
Hashed Block
Format
Hashed Block...
Unencrypted
Payload
(Optionally Compressed)
Unencrypted...
36
36
0
0
40
40
12
12
1
1
3
3
0
0
Byte
Offset
Byte...
Byte
Offset
Byte...
Byte
Offset
Byte...
Magic Number
Magic Number
Version
Version
Header
Header
Header
Header
Header
Header
Header 0
Header 0
...
...
0
0
8
8
32
32
Type
Type
Length
Length
Value
Value
Unencrypted
Unencr...
Length
Length
Header Format
Header Format
Hashed Block
Format
Hashed Block...
HMAC-SHA-256
HMAC-SHA-256
36
36
0
0
12
12
1
1
5
5
0
0
Byte
Offset
Byte...
Byte
Offset
Byte...
Byte
Offset
Byte...
KDBX 4 File
KDBX 4 File
As described at http://keepass.info/...
SHA-256
SHA-256
Encrypted
Payload
(Optionally Compressed)
Encrypted...
HMAC-SHA-256
HMAC-SHA-256
Inner Header
Inner Header
Hashed Blocks
Hashed Blocks
Inner Header
Inner Header
...
...
Header 0
Header 0
XML Database
XML Database
Hashed Blocks
Hashed Blocks
XML Database
XML Database
Copyright (c) 2023 Jo Rabin, Licensed under the Apache2 license. See KeePassJava2.
Copyright (c) 2023 Jo Rabin, Licensed under the Apache2 license. See KeePassJava2.
Text is not SVG - cannot display
\ No newline at end of file diff --git a/ModuleStructure.svg b/ModuleStructure.svg index 58d79fd0..d55374a0 100644 --- a/ModuleStructure.svg +++ b/ModuleStructure.svg @@ -1,4 +1,21 @@ + +
all
all
kdb
kdb
kdbx-io
kdbx-io
database
database
Test
Test
example
example
Other, future
Other,...
KDBX Implementations
KDBX I...
KDB Implementation
KDB Im...
???
???
Implementatiions
of the 
org.linguafranca.pwdb.Database Interface
Implem...
kdbx-database
kdbx-database
???
???
Text is not SVG - cannot display
\ No newline at end of file diff --git a/XSD/KDBX.3.1.xsd b/XSD/KDBX.3.1.xsd index ff22a5c7..be5c71f8 100644 --- a/XSD/KDBX.3.1.xsd +++ b/XSD/KDBX.3.1.xsd @@ -1,18 +1,19 @@ diff --git a/XSD/KDBX.4.1.reichl.xsd b/XSD/KDBX.4.1.reichl.xsd index a2f4780d..6ccdda40 100644 --- a/XSD/KDBX.4.1.reichl.xsd +++ b/XSD/KDBX.4.1.reichl.xsd @@ -1,3 +1,20 @@ + + diff --git a/all/src/main/java/org/linguafranca/all/IgnoreMe.java b/all/src/main/java/org/linguafranca/all/IgnoreMe.java index 463fbdbe..d2d2bb71 100644 --- a/all/src/main/java/org/linguafranca/all/IgnoreMe.java +++ b/all/src/main/java/org/linguafranca/all/IgnoreMe.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.all; /** diff --git a/all/src/main/java/org/linguafranca/all/package-info.java b/all/src/main/java/org/linguafranca/all/package-info.java index 83bf3905..83edcabd 100644 --- a/all/src/main/java/org/linguafranca/all/package-info.java +++ b/all/src/main/java/org/linguafranca/all/package-info.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + /** * This package/module provides a simple import of the database implementations of KeePass, "all" in the diagram below. *

diff --git a/database/pom.xml b/database/pom.xml index e501874f..3c1f1489 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -1,4 +1,21 @@ + + KeePassJava2.parent diff --git a/database/src/main/java/org/linguafranca/pwdb/Credentials.java b/database/src/main/java/org/linguafranca/pwdb/Credentials.java index 7f03b64e..58dc7a5b 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Credentials.java +++ b/database/src/main/java/org/linguafranca/pwdb/Credentials.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index 1d2cf0c8..f5ee971f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 1f69b842..63535b70 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index fd9b9d3b..9ba5f946 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/Icon.java b/database/src/main/java/org/linguafranca/pwdb/Icon.java index 304548b9..1d2599ac 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Icon.java +++ b/database/src/main/java/org/linguafranca/pwdb/Icon.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java index 19111bf3..f1073165 100644 --- a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java +++ b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb; import com.google.common.base.Charsets; diff --git a/database/src/main/java/org/linguafranca/pwdb/SerializableDatabase.java b/database/src/main/java/org/linguafranca/pwdb/SerializableDatabase.java index 74e7a961..a956839e 100644 --- a/database/src/main/java/org/linguafranca/pwdb/SerializableDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/SerializableDatabase.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/StreamConfiguration.java b/database/src/main/java/org/linguafranca/pwdb/StreamConfiguration.java index 4b717495..f6f3709f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/StreamConfiguration.java +++ b/database/src/main/java/org/linguafranca/pwdb/StreamConfiguration.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb; /** diff --git a/database/src/main/java/org/linguafranca/pwdb/StreamFormat.java b/database/src/main/java/org/linguafranca/pwdb/StreamFormat.java index bc8779bc..ed006e17 100644 --- a/database/src/main/java/org/linguafranca/pwdb/StreamFormat.java +++ b/database/src/main/java/org/linguafranca/pwdb/StreamFormat.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/Visitor.java b/database/src/main/java/org/linguafranca/pwdb/Visitor.java index e7e60e65..fe767f41 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Visitor.java +++ b/database/src/main/java/org/linguafranca/pwdb/Visitor.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java index 4e8a5608..6574dc26 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.base; diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java index 957599b2..aaead6c1 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.base; diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java index a525a36e..1b5ed2d5 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.base; diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index 3c0a915e..e647d4f5 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.protect; import org.linguafranca.pwdb.*; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/Aes.java b/database/src/main/java/org/linguafranca/pwdb/security/Aes.java index 776cf39e..51afece8 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/Aes.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/Aes.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.security; import org.bouncycastle.crypto.engines.AESEngine; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/Argon2.java b/database/src/main/java/org/linguafranca/pwdb/security/Argon2.java index a0ba2984..09d59494 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/Argon2.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/Argon2.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.security; import org.bouncycastle.crypto.generators.Argon2BytesGenerator; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/ChaCha.java b/database/src/main/java/org/linguafranca/pwdb/security/ChaCha.java index 65ad1f5b..fd281032 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/ChaCha.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/ChaCha.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.security; import org.bouncycastle.crypto.StreamCipher; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/CipherAlgorithm.java b/database/src/main/java/org/linguafranca/pwdb/security/CipherAlgorithm.java index bb1a1c0a..e1bc5524 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/CipherAlgorithm.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/CipherAlgorithm.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.security; import java.io.InputStream; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/Encryption.java b/database/src/main/java/org/linguafranca/pwdb/security/Encryption.java index 36d4a46e..32f9fdf8 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/Encryption.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/Encryption.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.security; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/KeyDerivationFunction.java b/database/src/main/java/org/linguafranca/pwdb/security/KeyDerivationFunction.java index 0e15663f..529dcfe8 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/KeyDerivationFunction.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/KeyDerivationFunction.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.security; import java.util.UUID; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/StreamEncryptor.java b/database/src/main/java/org/linguafranca/pwdb/security/StreamEncryptor.java index b3fc4a15..e8827590 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/StreamEncryptor.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/StreamEncryptor.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.security; diff --git a/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java b/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java index 0a95db47..a932d20a 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.security; import org.jetbrains.annotations.NotNull; diff --git a/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java b/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java index 0e9b269d..f8f7eaa0 100644 --- a/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java +++ b/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.util; import org.linguafranca.pwdb.PropertyValue; diff --git a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java index 94c61281..fc908a79 100644 --- a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb; import org.junit.Test; diff --git a/example/pom.xml b/example/pom.xml index cdac838e..02ea27e9 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -1,18 +1,19 @@ diff --git a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java index e016215a..652385bd 100644 --- a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java +++ b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.example; import io.github.novacrypto.SecureCharBuffer; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java index 1e78822b..651560dd 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx; import com.google.common.base.Strings; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java index 1bccac17..515c0211 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.Database; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index 5b3e93e3..de2b96d0 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.Database; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index b342f398..1c540f1c 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java index b0c3e7de..a21a8783 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java index 1174dbb9..758e11a2 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx; import com.google.common.io.CharStreams; diff --git a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java index 1448f9c7..aa6eec90 100644 --- a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.example; import org.junit.Test; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java index b0b7318b..3ab56a6f 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index b4dc0210..f4d09e8e 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java index 4a28b2fd..060bd566 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx; import org.junit.Test; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java index 9c0f7b01..ee20352e 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx.validation; import com.google.common.io.CharStreams; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java index d5f65d65..0a692bf4 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx.validation; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java index 339c7b5e..63c85322 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx.validation; import org.junit.Test; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java index 9819d53f..dfacfb90 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx.validation; import org.junit.Test; diff --git a/kdb/pom.xml b/kdb/pom.xml index b979631d..fff80ce3 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -1,4 +1,21 @@ + + KeePassJava2.parent diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java index b578d062..ce3326f4 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java index 88a2b56c..b6682a82 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index 20a27340..78fb9eff 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index f6877da2..9c5618b0 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbHeader.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbHeader.java index 976bf318..b26384e4 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbHeader.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbHeader.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbIcon.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbIcon.java index 911be794..e2077b11 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbIcon.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbIcon.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java index 51e357de..b31ef3e5 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseLoaderTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseLoaderTest.java index d375e45f..70a18b75 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseLoaderTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseLoaderTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java index 84d08972..4c23ea4d 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java index 06254062..14c063f1 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdb; diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java index c78ba10a..c07f87c0 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdb; import org.junit.Test; diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index 8ee2d613..43016227 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -1,18 +1,19 @@ diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index d4a5c928..8288effd 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index 724740a7..3d896503 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 6a39c0f3..5024f654 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxHistory.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxHistory.java index fd2fd91b..24797653 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxHistory.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxHistory.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxIcon.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxIcon.java index c1f34a74..b419b044 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxIcon.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxIcon.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index 016fb200..09406579 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java index 66eb416c..b4cac37e 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToByteConverter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java index 1b80f953..e83a1913 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/Base64ToUUIDConverter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java index ca1c4384..362352df 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/BooleanToStringConverter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java index fd613a9f..e09592d5 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ByteToBase64Converter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java index 2fb214b9..eadcde32 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/DateToStringConverter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java index 7b13542c..bdfea657 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToBooleanConverter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java index 07f80808..55a5f3ff 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java index e843a0b7..1feb3c8d 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/UUIDToBase64Converter.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java index 08294a07..86311aa0 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueDeserializer.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java index 3e25b25c..ce0fb5b4 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.converter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java index 309cc814..8b7b68c0 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.model; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java index 116a315e..3e46882f 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.model; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java index 0e1f8ba2..6fea2303 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/Times.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx.jackson.model; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java index 2add540b..7ee7e19c 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java index 794ed2a3..7d682a63 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java index c605d1b9..cdcbcd3f 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2024 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java index 71ed55e1..bd40b634 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java index 58050f24..4edafcbb 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java index 91217cc6..f00aeb6f 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java index 599b29a4..fa40a413 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java index 2ca89590..b63ad2c7 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2023 Giuseppe Valente + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb; diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index e95b04f6..b82da458 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -1,18 +1,19 @@ diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java index 8285e8e9..edee8424 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java index ea3551fd..d0497472 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java index 1ebeb464..9577f983 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxHeader.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxHeader.java index fb4e490e..1dc89685 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxHeader.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxHeader.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxKeyFile.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxKeyFile.java index 33093aca..960572f1 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxKeyFile.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxKeyFile.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java index 5a443231..bedb5bb8 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java index da0fb9bf..85a59ceb 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxStreamFormat.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java index e301e625..493b4827 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.hashedblock; import org.jetbrains.annotations.NotNull; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java index 90919b20..54dbe3c3 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.hashedblock; import org.jetbrains.annotations.NotNull; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java index 47d96986..e3b30e2a 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.hashedblock; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java index e376e556..283d4b95 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.hashedblock; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java index b08c0bad..f411197f 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.hashedblock; import com.google.common.io.LittleEndianDataInputStream; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java index 14b4fa64..6dfc22a7 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.hashedblock; import static org.linguafranca.pwdb.format.Helpers.toBytes; diff --git a/kdbx-io/src/main/resources/base.kdbx.xml b/kdbx-io/src/main/resources/base.kdbx.xml index 5a791ae4..b32e337e 100644 --- a/kdbx-io/src/main/resources/base.kdbx.xml +++ b/kdbx-io/src/main/resources/base.kdbx.xml @@ -1,17 +1,18 @@ diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java index 14d21611..4846da4d 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.hashedblock; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java index 1205d30d..5c66459a 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java index 570f005f..e6676cd2 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx; import org.junit.Test; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java index 58c7a965..7a30dc68 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.kdbx; import com.google.common.io.CharStreams; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java index 603a93a7..e5fad1a8 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java index 4a93498c..53998b81 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.kdbx; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java index 30a77ed6..4cca6eb8 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java @@ -1,17 +1,18 @@ /* - * Copyright 2015 Jo Rabin + * Copyright (c) 2025. Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. + * */ package org.linguafranca.pwdb.security; diff --git a/logback.xml b/logback.xml index 972c5a5f..566fa576 100644 --- a/logback.xml +++ b/logback.xml @@ -1,3 +1,20 @@ + + diff --git a/test/pom.xml b/test/pom.xml index a86780c2..b7d513dc 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -1,4 +1,21 @@ + + KeePassJava2.parent diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java index eac71040..6353bfcf 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java index 531a4772..9e4b9bf0 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java index a870ec18..2cf90c57 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java index 95343a5b..43b9ee69 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java index 691c3877..b76b7986 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.checks; import org.junit.Test; diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java index 1a330df6..e4064799 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java +++ b/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.pwdb.checks;/* * Copyright 2015 Jo Rabin * diff --git a/test/src/test/resources/Attachment.xml b/test/src/test/resources/Attachment.xml index 5bdd5620..0bfa905f 100755 --- a/test/src/test/resources/Attachment.xml +++ b/test/src/test/resources/Attachment.xml @@ -1,4 +1,21 @@ + + KeePass diff --git a/test/src/test/resources/ExampleDatabase.xml b/test/src/test/resources/ExampleDatabase.xml index 6535b834..0f77e5cc 100755 --- a/test/src/test/resources/ExampleDatabase.xml +++ b/test/src/test/resources/ExampleDatabase.xml @@ -1,4 +1,21 @@ + + KeePass diff --git a/test/src/test/resources/kdbxSample.xml b/test/src/test/resources/kdbxSample.xml index 9ff1df98..5a6f513a 100644 --- a/test/src/test/resources/kdbxSample.xml +++ b/test/src/test/resources/kdbxSample.xml @@ -1,18 +1,19 @@ diff --git a/test/src/test/resources/xml/V4-AES-AES.xml b/test/src/test/resources/xml/V4-AES-AES.xml index a8cf1f9f..d312e5de 100644 --- a/test/src/test/resources/xml/V4-AES-AES.xml +++ b/test/src/test/resources/xml/V4-AES-AES.xml @@ -1,4 +1,21 @@ + + KeePass diff --git a/test/src/test/resources/xml/V4-AES-Argon2.xml b/test/src/test/resources/xml/V4-AES-Argon2.xml index 3ebf3a01..9fc08870 100644 --- a/test/src/test/resources/xml/V4-AES-Argon2.xml +++ b/test/src/test/resources/xml/V4-AES-Argon2.xml @@ -1,4 +1,21 @@ + + KeePass diff --git a/test/src/test/resources/xml/V4-ChaCha20-AES.xml b/test/src/test/resources/xml/V4-ChaCha20-AES.xml index 1b687758..8dee14dd 100644 --- a/test/src/test/resources/xml/V4-ChaCha20-AES.xml +++ b/test/src/test/resources/xml/V4-ChaCha20-AES.xml @@ -1,4 +1,21 @@ + + KeePass diff --git a/test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml b/test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml index 1a6574c2..d182609e 100644 --- a/test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml +++ b/test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml @@ -1,4 +1,21 @@ + + KeePass diff --git a/util/pom.xml b/util/pom.xml index 234243b6..1aec9411 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -1,4 +1,21 @@ + + diff --git a/util/src/main/java/org/linguafranca/util/HexViewer.java b/util/src/main/java/org/linguafranca/util/HexViewer.java index b9c17353..0f29c7fd 100644 --- a/util/src/main/java/org/linguafranca/util/HexViewer.java +++ b/util/src/main/java/org/linguafranca/util/HexViewer.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.util; import java.io.IOException; diff --git a/util/src/main/java/org/linguafranca/util/MavenRunListener.java b/util/src/main/java/org/linguafranca/util/MavenRunListener.java index 1839a645..dc5f9ba1 100644 --- a/util/src/main/java/org/linguafranca/util/MavenRunListener.java +++ b/util/src/main/java/org/linguafranca/util/MavenRunListener.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.util; import org.junit.runner.Description; diff --git a/util/src/main/java/org/linguafranca/util/TestUtil.java b/util/src/main/java/org/linguafranca/util/TestUtil.java index f6883ecf..1fbcd3a7 100644 --- a/util/src/main/java/org/linguafranca/util/TestUtil.java +++ b/util/src/main/java/org/linguafranca/util/TestUtil.java @@ -1,3 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + package org.linguafranca.util; import java.io.IOException; From 97c4e6773c8ff61e2f28c0d1e32dc340b27a8c86 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sat, 8 Mar 2025 13:32:21 +0000 Subject: [PATCH 33/66] fluent building --- .../java/org/linguafranca/pwdb/Entry.java | 77 ++++++++++++++++++- .../java/org/linguafranca/pwdb/Group.java | 9 +++ .../linguafranca/pwdb/base/AbstractEntry.java | 25 ++++++ .../linguafranca/pwdb/base/AbstractGroup.java | 13 +++- .../pwdb/protect/ProtectedDatabase.java | 2 +- .../org/linguafranca/pwdb/kdbx/Fluent.java | 18 +++-- .../linguafranca/pwdb/kdbx/QuickStart.java | 36 ++++++++- .../pwdb/kdbx/SimpleQuickStartTest.java | 6 +- .../pwdb/kdbx/jackson/KdbxEntry.java | 25 ++++++ v3.md | 28 +++++++ 10 files changed, 219 insertions(+), 20 deletions(-) create mode 100644 v3.md diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 63535b70..321b1c5c 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -56,6 +56,15 @@ public interface Entry { String STANDARD_PROPERTY_NAME_TITLE = "Title"; String STANDARD_PROPERTY_NAME_NOTES = "Notes"; + /** allows for static import of name */ + interface STANDARD_PROPERTY_NAME { + String USER_NAME = STANDARD_PROPERTY_NAME_USER_NAME; + String PASSWORD = STANDARD_PROPERTY_NAME_PASSWORD; + String URL = STANDARD_PROPERTY_NAME_URL; + String TITLE = STANDARD_PROPERTY_NAME_TITLE; + String NOTES = STANDARD_PROPERTY_NAME_NOTES; + } + List STANDARD_PROPERTY_NAMES = List.of(STANDARD_PROPERTY_NAME_USER_NAME, STANDARD_PROPERTY_NAME_PASSWORD, STANDARD_PROPERTY_NAME_URL, @@ -119,7 +128,8 @@ interface Matcher { PropertyValue getPropertyValue(String name); /** - * Sets the value of a property. Use of this method is not recommended for fields with protected values. + * Sets the value of a property as a String in memory. + * Use of this method is not recommended for fields with protected values. * Use {@link #setPropertyValue(String, PropertyValue)} * *

Other than the {@link #STANDARD_PROPERTY_NAMES} support for this method is optional. @@ -133,7 +143,7 @@ interface Matcher { Entry setProperty(String name, String value); /** - * Sets the value of a property as a property value. The method does not check whether the + * Sets the value of a property as a property value in memory. The method does not check whether the * passed PropertyValue is protected relative to {@link Database#shouldProtect(String)} * *

Other than the {@link #STANDARD_PROPERTY_NAMES} support for this method is optional. @@ -147,7 +157,68 @@ interface Matcher { Entry setPropertyValue(String name, PropertyValue value); /** - * Removes this non-standard property, if it exists. + * Adds (or modifies) the value of a property as a property value in memory. The method uses the + * PropertyValue.Strategy to determine how to determine the storage format in memory. + * + *

Other than the {@link #STANDARD_PROPERTY_NAMES} support for this method is optional. + * + * @param name the name of the property to set + * @param value the value to set it to + * @throws UnsupportedOperationException if the name is not one of the standard properties and + * non-standard properties are not supported + * @see Database#supportsNonStandardPropertyNames() + */ + Entry addProperty(String name, byte[] value); + + /** + * Adds (or modifies) the value of a property as a property value in memory. The method uses the + * PropertyValue.Strategy to determine how to determine the storage format in memory. + * + *

Other than the {@link #STANDARD_PROPERTY_NAMES} support for this method is optional. + * + * @param name the name of the property to set + * @param value the value to set it to + * @throws UnsupportedOperationException if the name is not one of the standard properties and + * non-standard properties are not supported + * @see Database#supportsNonStandardPropertyNames() + */ + Entry addProperty(String name, char[] value); + + /** + * Adds (or modifies) the value of a property as a property value in memory. The method uses the + * PropertyValue.Strategy to determine how to determine the storage format in memory. + * + *

Other than the {@link #STANDARD_PROPERTY_NAMES} support for this method is optional. + * + * @param name the name of the property to set + * @param value the value to set it to + * @throws UnsupportedOperationException if the name is not one of the standard properties and + * non-standard properties are not supported + * @see Database#supportsNonStandardPropertyNames() + */ + Entry addProperty(String name, CharSequence value); + + /** + * Adds a newly created entry to the Group parent of the current Entry, if there is one. Intended primary for + * fluid building of entries within a group. + * + * @throws IllegalStateException if there is no parent + * @param name the title of the entry + * @return the newly created entry + */ + Entry addEntry(String name); + + /** + * Adds a newly created entry to the Group parent of the current Entry, if there is one. Intended primary for + * fluid building of entries within a group. + * + * @throws IllegalStateException if there is no parent + * @return the newly created entry + */ + Entry addEntry(); + + /** + * Removes this property, if it exists and if it is a non-standard property * * @return true if the property exists and was removed, false otherwise * @param name the value of the property to remove diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index 9ba5f946..4820c4bb 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -22,6 +22,7 @@ import javax.annotation.Nullable; import java.util.List; import java.util.UUID; +import java.util.function.Function; /** * Interface for a Database Group. @@ -93,6 +94,14 @@ public interface Group { */ Group addGroup(Group group); + /** + * Create a new group in this database with the name provided and add it to the group + * + * @param name the name of the group to add + * @return the group added + */ + Group addGroup(String name); + /** * Returns a list of child Groups whose name exactly matches that supplied. * diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java index aaead6c1..6cae57ec 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java @@ -135,5 +135,30 @@ public Entry setPropertyValue(String name, PropertyValue value) { throw new UnsupportedOperationException(); } + public Entry addProperty(String name, byte[] value){ + throw new UnsupportedOperationException(); + } + public Entry addProperty(String name, char[] value){ + throw new UnsupportedOperationException(); + } + public Entry addProperty(String name, CharSequence value){ + throw new UnsupportedOperationException(); + } + + public Entry addEntry(){ + if (this.getParent()!=null) { + return this.getParent().addEntry(); + } + throw new IllegalStateException("Entry has no group parent"); + } + + public Entry addEntry(String title){ + if (this.getParent()!=null) { + return this.getParent().addEntry(title); + } + throw new IllegalStateException("Entry has no group parent"); + } + + protected abstract void touch(); } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java index 1b5ed2d5..c949ca27 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java @@ -31,13 +31,22 @@ */ public abstract class AbstractGroup implements Group { + @Override public Entry addEntry() { - return getDatabase().newEntry(); + Entry entry = getDatabase().newEntry(); + return addEntry(entry); } @Override public Entry addEntry(String title) { - return getDatabase().newEntry(title); + Entry entry = getDatabase().newEntry(title); + return addEntry(entry); + } + + @Override + public Group addGroup(String name) { + Group group = getDatabase().newGroup(name); + return addGroup(group); } @Override diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index e647d4f5..2da5cda0 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -27,7 +27,7 @@ * Base class for Databases which support storage using {@link PropertyValue}s. *

* By default {@link Entry#STANDARD_PROPERTY_NAME_PASSWORD} is defined as protected and the property value strategy - * establishes {@link org.linguafranca.pwdb.PropertyValue.StringStore} storage for unprotected values and + * establishes {@link org.linguafranca.pwdb.PropertyValue.BytesStore} storage for unprotected values and * {@link org.linguafranca.pwdb.PropertyValue.SealedStore} for protected values. */ public abstract class ProtectedDatabase extends AbstractDatabase { diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java index 515c0211..ff5fdbe8 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java @@ -18,22 +18,24 @@ package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; public class Fluent { public void fluentExample() { Database database = new KdbxDatabase(); - database.newGroup("Group 1") - .addEntry() - .setProperty("prop1", "value1") - .setProperty("prop2", "value2") - .setProperty("prop3", "value3") - .getParent() - .addEntry() + database.getRootGroup().addGroup("Group 1") + .addEntry("as property values determined by the database property value strategy (preferred)") + .addProperty("prop1", "value1".getBytes()) + .addProperty("prop2", "value2") + .addProperty("prop3", "value3") + .addEntry("as explicit property values") + .setPropertyValue("prop1", new PropertyValue.BytesStore("value1".getBytes())) + .setPropertyValue("prop2", new PropertyValue.SealedStore("value2")) + .addEntry("as strings") .setProperty("prop1", "value1") .setProperty("prop2", "value2") .setProperty("prop3", "value3"); - } } diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 1c540f1c..282e9945 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -31,6 +31,7 @@ import java.nio.file.Path; import java.util.List; +import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** @@ -44,6 +45,36 @@ public abstract class QuickStart { public static final String TEST_OUTPUT_DIR = "testOutput/"; static PrintStream printStream = getTestPrintStream(); + public void canonicalQuickStart() throws IOException { + + // V3 create a database, no need to specify what kind (e.g. JacksonDatabase) + // V3 Database no longer need to be generified + Database database = new KdbxDatabase(); + // V3 the property value strategy is set to PropertyValue.Strategy.Default(), anyway, but the Default property + // value strategy being to protect field "Password" and no other, all of which are stored as byte[] + database.setPropertyValueStrategy(new PropertyValue.Strategy.Default()); + // V3 Group is not generified. create a group + // V3 add a new group to another group with the name supplied + Group main = database.getRootGroup().addGroup("Main"); + // V3 fluent building of Entry (using PropertyValueStrategy as set in database to determine protection) + main.addEntry() + // V3 addProperty short-cut for setPropertyValue + // V3 abbreviated static import of standard field names + .addProperty(TITLE, "first entry") + .addProperty(USER_NAME, "Tom") + .addProperty(PASSWORD, "123".getBytes()) + .addProperty(URL, "http://localhost:8080/") + .addEntry("2nd Entry") // v3 addEntry on Entry adds Entry to parent Group if exists + .addProperty(USER_NAME, "Alice") + .addProperty(PASSWORD, "123".getBytes()); + + // V3 KdbxCreds now called KdbxCredentials + KdbxCredentials credentials = new KdbxCredentials("123".getBytes()); + try (OutputStream outputStream = Files.newOutputStream(Path.of(TEST_OUTPUT_DIR, "test.kdbx"))) { + database.save(credentials, outputStream); + } + } + /** * Load KDBX */ @@ -98,7 +129,8 @@ public void splice(Group newParent, Group groupToSplice) { * Group by title */ public Group groupByTitle(Database database) { - List entries = database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); + List entries = + database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); // create a new group using DB factory method Group newParent = database.newGroup("Found entries"); // iterate over the found entries @@ -114,7 +146,7 @@ public Group groupByTitle(Database database) { /** * Load KDB and save as KDBX */ - public void loadKdb() throws IOException { + public void loadKdbSaveKdbx() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); // password credentials Credentials credentials = new KdbCredentials.Password("123".getBytes()); diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index f4d09e8e..b856804f 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -19,14 +19,12 @@ import org.junit.BeforeClass; import org.junit.Test; -import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.format.KdbxCreds; import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.security.Encryption; import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -57,8 +55,8 @@ public void loadTest() throws IOException { } @Test - public void loadKdbSaveVernacular() throws IOException { - super.loadKdb(); + public void loadKdbSaveKdbx() throws IOException { + super.loadKdbSaveKdbx(); } @Test diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index 3d896503..d0ba27db 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; +import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.base.AbstractEntry; @@ -197,6 +198,30 @@ public KdbxEntry setPropertyValue(String name, PropertyValue value) { return this; } + @Override + @JsonIgnore + public Entry addProperty(String name, byte[] value){ + PropertyValue.Factory f = database.getPropertyValueStrategy().getFactoryFor(name); + this.setPropertyValue(name, f.of(value)); + return this; + } + + @Override + @JsonIgnore + public Entry addProperty(String name, char[] value){ + PropertyValue.Factory f = database.getPropertyValueStrategy().getFactoryFor(name); + this.setPropertyValue(name, f.of(value)); + return this; + } + + @Override + @JsonIgnore + public Entry addProperty(String name, CharSequence value){ + PropertyValue.Factory f = database.getPropertyValueStrategy().getFactoryFor(name); + this.setPropertyValue(name, f.of(value)); + return this; + } + @Override @JsonIgnore public boolean removeProperty(String name) throws IllegalArgumentException { diff --git a/v3.md b/v3.md new file mode 100644 index 00000000..806d97a6 --- /dev/null +++ b/v3.md @@ -0,0 +1,28 @@ +``` java + public void canonicalQuickStart() throws IOException { + + // V3 create a database, no need to specify what kind, there is only one kind + // V3 Database no longer need to be generified + Database database = new KdbxDatabase(); + // V3 the property value strategy is set to this, anyway, but the Default property value strategy is to protect + // field "Password" and no other, all of which are stored as byte[] + database.setPropertyValueStrategy(new PropertyValue.Strategy.Default()); + // create a group (as before) + Group main = database.newGroup("Main"); + // add to root (as before) + database.getRootGroup().addGroup(main); + // V3 fluent building of Entry (using PropertyValueStrategy as set in database to determine protection) + main.addEntry("entry") + // V3 abbreviated static import of standard field names + .addProperty(USER_NAME, "Tom") + .addProperty(PASSWORD, "123".getBytes()) + .addProperty(URL, "http://localhost:8080/"); + + + // V3 KdbxCreds now called KdbxCredentials + KdbxCredentials credentials = new KdbxCredentials("123".getBytes()); + try (OutputStream outputStream = Files.newOutputStream(Path.of(TEST_OUTPUT_DIR,"test.kdbx"))) { + database.save(credentials, outputStream); + } + } + ``` \ No newline at end of file From 6e38b679bbca42affc4987319629e414204a4cda Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 10 Mar 2025 11:29:45 +0000 Subject: [PATCH 34/66] Refactoring and renaming of base classes --- database/pom.xml | 6 - .../AbstractDatabase.java | 2 +- .../{base => abstractdb}/AbstractEntry.java | 2 +- .../{base => abstractdb}/AbstractGroup.java | 2 +- .../pwdb/basic/BasicDatabase.java | 24 +++ .../linguafranca/pwdb/basic/BasicEntry.java | 23 +++ .../linguafranca/pwdb/basic/BasicGroup.java | 144 ++++++++++++++++ .../pwdb/protect/ProtectedDatabase.java | 2 +- .../linguafranca/pwdb/PropertyValueTest.java | 2 +- .../linguafranca/pwdb}/PropertyValueUtil.java | 4 +- .../pwdb/protect/ProtectedDatabaseTest.java | 163 ++++++++++++++++++ .../linguafranca/pwdb/kdb/KdbDatabase.java | 2 +- .../org/linguafranca/pwdb/kdb/KdbEntry.java | 2 +- .../org/linguafranca/pwdb/kdb/KdbGroup.java | 4 +- .../pwdb/kdbx/jackson/KdbxEntry.java | 2 +- .../pwdb/kdbx/jackson/KdbxGroup.java | 2 +- pom.xml | 24 ++- 17 files changed, 387 insertions(+), 23 deletions(-) rename database/src/main/java/org/linguafranca/pwdb/{base => abstractdb}/AbstractDatabase.java (99%) rename database/src/main/java/org/linguafranca/pwdb/{base => abstractdb}/AbstractEntry.java (99%) rename database/src/main/java/org/linguafranca/pwdb/{base => abstractdb}/AbstractGroup.java (98%) create mode 100644 database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java create mode 100644 database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java create mode 100644 database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java rename database/src/{main/java/org/linguafranca/pwdb/util => test/java/org/linguafranca/pwdb}/PropertyValueUtil.java (96%) create mode 100644 database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java diff --git a/database/pom.xml b/database/pom.xml index 3c1f1489..e85192fd 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -43,11 +43,5 @@ org.bouncycastle bcpkix-jdk18on - - - junit - junit - provided - \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java similarity index 99% rename from database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java rename to database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java index 6574dc26..80979641 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.base; +package org.linguafranca.pwdb.abstractdb; import org.linguafranca.pwdb.*; diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractEntry.java similarity index 99% rename from database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java rename to database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractEntry.java index 6cae57ec..145617f1 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractEntry.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.base; +package org.linguafranca.pwdb.abstractdb; import org.linguafranca.pwdb.*; diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractGroup.java similarity index 98% rename from database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java rename to database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractGroup.java index c949ca27..3e9b4cc0 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractGroup.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.base; +package org.linguafranca.pwdb.abstractdb; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java new file mode 100644 index 00000000..7f416bb5 --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.linguafranca.pwdb.protect.ProtectedDatabase; + +public class BasicDatabase implements ProtectedDatabase { + // please implement a basic database here +} diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java new file mode 100644 index 00000000..d3f2f44f --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.linguafranca.pwdb.abstractdb.AbstractEntry; + +public class BasicEntry extends AbstractEntry { +} diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java new file mode 100644 index 00000000..b394633d --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; +import org.linguafranca.pwdb.Icon; +import org.linguafranca.pwdb.abstractdb.AbstractGroup; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.UUID; + +public class BasicGroup extends AbstractGroup { + private BasicGroup parent; + private final List groups = new ArrayList<>(); + private final List entries = new ArrayList<>(); + private String name; + private Icon icon; + private final UUID uuid; + private final @NotNull BasicDatabase database; + + BasicGroup(@NotNull BasicDatabase database) { + this.database = database; + this.uuid = UUID.randomUUID(); + } + + BasicGroup(@NotNull BasicDatabase database, String name) { + this(database); + this.name = name; + } + + @Override + public boolean isRootGroup() { + return false; + } + + @Override + public boolean isRecycleBin() { + return false; + } + + @Override + public @Nullable Group getParent() { + return parent; + } + + @Override + public void setParent(Group parent) { + this.parent = (BasicGroup) parent; + } + + @Override + public List getGroups() { + return Collections.unmodifiableList(groups); + } + + @Override + public int getGroupsCount() { + return groups.size(); + } + + @Override + public Group addGroup(Group group) { + groups.add((BasicGroup) group); + return group; + } + + @Override + public Group removeGroup(Group group) { + groups.remove((BasicGroup) group); + return group; + } + + @Override + public List getEntries() { + return Collections.unmodifiableList(entries); + } + + @Override + public int getEntriesCount() { + return entries.size(); + } + + @Override + public Entry addEntry(Entry entry) { + entries.add((BasicEntry) entry); + return entry; + } + + @Override + public Entry removeEntry(Entry entry) { + return entry; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public void setName(String name) { + this.name=name; + } + + @Override + public UUID getUuid() { + return this.uuid; + } + + @Override + public Icon getIcon() { + return this.icon; + } + + @Override + public void setIcon(Icon icon) { + this.icon = icon; + } + + @Override + public @NotNull Database getDatabase() { + return this.database; + } +} diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index 2da5cda0..5e98092f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -18,7 +18,7 @@ package org.linguafranca.pwdb.protect; import org.linguafranca.pwdb.*; -import org.linguafranca.pwdb.base.AbstractDatabase; +import org.linguafranca.pwdb.abstractdb.AbstractDatabase; import java.util.Collections; import java.util.List; diff --git a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java index fc908a79..578e3ab5 100644 --- a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java @@ -22,7 +22,7 @@ import java.nio.charset.StandardCharsets; import static org.junit.Assert.*; -import static org.linguafranca.pwdb.util.PropertyValueUtil.genericTest; +import static org.linguafranca.pwdb.PropertyValueUtil.genericTest; public class PropertyValueTest { diff --git a/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueUtil.java similarity index 96% rename from database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java rename to database/src/test/java/org/linguafranca/pwdb/PropertyValueUtil.java index f8f7eaa0..ab071e6c 100644 --- a/database/src/main/java/org/linguafranca/pwdb/util/PropertyValueUtil.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueUtil.java @@ -15,9 +15,7 @@ * */ -package org.linguafranca.pwdb.util; - -import org.linguafranca.pwdb.PropertyValue; +package org.linguafranca.pwdb; import java.nio.charset.StandardCharsets; diff --git a/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java new file mode 100644 index 00000000..0e640d4e --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.protect; + +import org.jetbrains.annotations.Nullable; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.*; +import org.linguafranca.pwdb.protect.ProtectedDatabase; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class ProtectedDatabaseTest { + + private ProtectedDatabase database; + + @BeforeEach + void setUp() { + database = new ProtectedDatabase() { + @Override + public Group getRootGroup() { + return null; + } + + @Override + public Group newGroup() { + return null; + } + + @Override + public Entry newEntry() { + return null; + } + + @Override + public Icon newIcon() { + return null; + } + + @Override + public Icon newIcon(Integer i) { + return null; + } + + @Override + public boolean isRecycleBinEnabled() { + return false; + } + + @Override + public void enableRecycleBin(boolean enable) { + + } + + @Override + public @Nullable Group getRecycleBin() { + return null; + } + + @Override + public String getName() { + return ""; + } + + @Override + public void setName(String name) { + + } + + @Override + public String getDescription() { + return ""; + } + + @Override + public void setDescription(String description) { + + } + + @Override + public void save(Credentials credentials, OutputStream outputStream) throws IOException { + + } + + @Override + public void save(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) throws IOException { + + } + + @Override + public StreamFormat getStreamFormat() { + return null; + } + // Implement abstract methods if necessary for testing + }; + } + + @Test + void testShouldProtect() { + String propertyName = "password"; + database.setShouldProtect(propertyName, true); + assertTrue(database.shouldProtect(propertyName)); + } + + @Test + void testSetShouldProtect() { + String propertyName = "password"; + database.setShouldProtect(propertyName, true); + assertTrue(database.shouldProtect(propertyName)); + + database.setShouldProtect(propertyName, false); + assertFalse(database.shouldProtect(propertyName)); + } + + @Test + void testListShouldProtect() { + String propertyName1 = "password"; + String propertyName2 = "username"; + database.setShouldProtect(propertyName1, true); + database.setShouldProtect(propertyName2, true); + + List protectedProperties = database.listShouldProtect(); + assertTrue(protectedProperties.contains(propertyName1)); + assertTrue(protectedProperties.contains(propertyName2)); + } + + @Test + void testGetPropertyValueStrategy() { + PropertyValue.Strategy strategy = database.getPropertyValueStrategy(); + assertNotNull(strategy); + } + + @Test + void testSetPropertyValueStrategy() { + PropertyValue.Strategy newStrategy = new PropertyValue.Strategy.Default(); + database.setPropertyValueStrategy(newStrategy); + assertEquals(newStrategy, database.getPropertyValueStrategy()); + } + + @Test + void testSupportsPropertyValueStrategy() { + assertTrue(database.supportsPropertyValueStrategy()); + } +} \ No newline at end of file diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java index b6682a82..11c2575b 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java @@ -18,7 +18,7 @@ package org.linguafranca.pwdb.kdb; import org.linguafranca.pwdb.*; -import org.linguafranca.pwdb.base.AbstractDatabase; +import org.linguafranca.pwdb.abstractdb.AbstractDatabase; import java.io.IOException; import java.io.InputStream; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index 78fb9eff..0e7b22eb 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -21,7 +21,7 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractEntry; +import org.linguafranca.pwdb.abstractdb.AbstractEntry; import java.util.ArrayList; import java.util.Date; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index 9c5618b0..889892c6 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -21,8 +21,8 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractEntry; -import org.linguafranca.pwdb.base.AbstractGroup; +import org.linguafranca.pwdb.abstractdb.AbstractEntry; +import org.linguafranca.pwdb.abstractdb.AbstractGroup; import org.linguafranca.pwdb.Entry; import java.util.ArrayList; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index d0ba27db..f9427f3b 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -27,7 +27,7 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.PropertyValue; -import org.linguafranca.pwdb.base.AbstractEntry; +import org.linguafranca.pwdb.abstractdb.AbstractEntry; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 5024f654..ad599da4 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -28,7 +28,7 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractGroup; +import org.linguafranca.pwdb.abstractdb.AbstractGroup; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.StringToBooleanConverter; diff --git a/pom.xml b/pom.xml index 70086b4c..209c2fde 100644 --- a/pom.xml +++ b/pom.xml @@ -80,6 +80,9 @@ 3.4.0 2.18.0 3.5.0 + + 5.8.1 + 1.8.2 false @@ -199,9 +202,24 @@ provided - junit - junit - test + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + + + org.junit.vintage + junit-vintage-engine + ${junit.version} + + + org.junit.platform + junit-platform-launcher + ${junit.platform.version} + + + org.junit.platform + junit-platform-runner + ${junit.platform.version} org.slf4j From 0a14d53f3051e64f2d8670e0bc958ee6d95f2a44 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 10 Mar 2025 12:59:09 +0000 Subject: [PATCH 35/66] Make remove all ? extends from List return values --- .../java/org/linguafranca/pwdb/Database.java | 4 +- .../java/org/linguafranca/pwdb/Group.java | 11 +++-- .../pwdb/base/AbstractDatabase.java | 4 +- .../linguafranca/pwdb/base/AbstractGroup.java | 6 +-- .../org/linguafranca/pwdb/kdbx/Fluent.java | 41 ------------------- .../linguafranca/pwdb/kdbx/QuickStart.java | 30 +++++++++++++- .../org/linguafranca/pwdb/kdb/KdbGroup.java | 10 ++--- .../linguafranca/pwdb/kdb/KdbSerializer.java | 4 +- .../pwdb/kdbx/jackson/KdbxGroup.java | 4 +- 9 files changed, 49 insertions(+), 65 deletions(-) delete mode 100644 example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index f5ee971f..421b24d5 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -193,7 +193,7 @@ public interface Database { * @param matcher the matcher to use * @return a list of entries */ - List findEntries(Entry.Matcher matcher); + List findEntries(Entry.Matcher matcher); /** * Find all entries that match {@link Entry#match(String)} @@ -201,7 +201,7 @@ public interface Database { * @param find string to find * @return a list of entries */ - List findEntries(String find); + List findEntries(String find); /** * Gets the name of the database or null if not supported diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index 4820c4bb..6dee7d9a 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -22,7 +22,6 @@ import javax.annotation.Nullable; import java.util.List; import java.util.UUID; -import java.util.function.Function; /** * Interface for a Database Group. @@ -73,7 +72,7 @@ public interface Group { * * @return a modifiable list of groups */ - List getGroups(); + List getGroups(); /** * Returns the number of groups that are direct children of this group @@ -110,7 +109,7 @@ public interface Group { * @param groupName the name of the groups sought * @return a modifiable list */ - List findGroups(String groupName); + List findGroups(String groupName); /** * Removes the group supplied from this group. The group removed @@ -129,7 +128,7 @@ public interface Group { /** * Returns a modifiable by the caller list of entries contained in this group. */ - List getEntries(); + List getEntries(); /** * Returns the number of entries in this group @@ -152,7 +151,7 @@ public interface Group { * @return a modifiable-by-caller list * @see Entry#match(String) */ - List findEntries(String match, boolean recursive); + List findEntries(String match, boolean recursive); /** * Finds all entries in this group that match using the matcher supplied. @@ -167,7 +166,7 @@ public interface Group { * @return a modifiable-by-caller list * @see Entry#match(Entry.Matcher) */ - List findEntries(Entry.Matcher matcher, boolean recursive); + List findEntries(Entry.Matcher matcher, boolean recursive); /** * Adds an entry to this group removing it from another group diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java index 6574dc26..e873d723 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java @@ -71,12 +71,12 @@ public void visit(Group group, Visitor visitor) { } @Override - public List findEntries(Entry.Matcher matcher) { + public List findEntries(Entry.Matcher matcher) { return getRootGroup().findEntries(matcher, true); } @Override - public List findEntries(String find) { + public List findEntries(String find) { return getRootGroup().findEntries(find, true); } diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java index c949ca27..227208f6 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java @@ -50,7 +50,7 @@ public Group addGroup(String name) { } @Override - public List findGroups(String group) { + public List findGroups(String group) { ArrayList result = new ArrayList<>(); for (Group g: getGroups()) { if (g.getName().equals(group)) { @@ -61,12 +61,12 @@ public List findGroups(String group) { } @Override - public List findEntries(String find, boolean recursive) { + public List findEntries(String find, boolean recursive) { return findEntries(entry -> entry.match(find), recursive); } @Override - public List findEntries(Entry.Matcher matcher, boolean recursive) { + public List findEntries(Entry.Matcher matcher, boolean recursive) { List result = new ArrayList<>(getEntries().size()); for (Entry entry: getEntries()){ if (entry.match(matcher)){ diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java deleted file mode 100644 index ff5fdbe8..00000000 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Fluent.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.kdbx; - -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.PropertyValue; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; - -public class Fluent { - - public void fluentExample() { - Database database = new KdbxDatabase(); - database.getRootGroup().addGroup("Group 1") - .addEntry("as property values determined by the database property value strategy (preferred)") - .addProperty("prop1", "value1".getBytes()) - .addProperty("prop2", "value2") - .addProperty("prop3", "value3") - .addEntry("as explicit property values") - .setPropertyValue("prop1", new PropertyValue.BytesStore("value1".getBytes())) - .setPropertyValue("prop2", new PropertyValue.SealedStore("value2")) - .addEntry("as strings") - .setProperty("prop1", "value1") - .setProperty("prop2", "value2") - .setProperty("prop3", "value3"); - } -} diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 282e9945..f1a64c62 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -68,6 +68,9 @@ public void canonicalQuickStart() throws IOException { .addProperty(USER_NAME, "Alice") .addProperty(PASSWORD, "123".getBytes()); + // find entries just added, in V3 the findEntries return value is just List + List entries = database.findEntries("entry"); + // V3 KdbxCreds now called KdbxCredentials KdbxCredentials credentials = new KdbxCredentials("123".getBytes()); try (OutputStream outputStream = Files.newOutputStream(Path.of(TEST_OUTPUT_DIR, "test.kdbx"))) { @@ -75,6 +78,31 @@ public void canonicalQuickStart() throws IOException { } } + /** + * Illustrates the fluent API for creating a database, as well as the various ways of setting property values. + */ + public void fluentExample() { + Database database = new KdbxDatabase(); + database.getRootGroup().addGroup("Group 1") + .addEntry("as property values determined by the database property value strategy (preferred)") + .addProperty("prop1", "value1".getBytes()) + .addProperty("prop2", "value2") + .addProperty("prop3", "value3") + .addEntry("as non default storage type (according to PropertyValueStrategy) property values") + .setPropertyValue("prop1", database.getPropertyValueStrategy().newProtected().of("value1")) + .setPropertyValue("prop2", database.getPropertyValueStrategy().newUnprotected().of("value2")) + .addEntry("as explicit storage type property values") + .setPropertyValue("prop1", new PropertyValue.BytesStore("value1".getBytes())) + .setPropertyValue("prop2", new PropertyValue.SealedStore("value2")) + .addEntry("as strings") + .setProperty("prop1", "value1") + .setProperty("prop2", "value2") + .setProperty("prop3", "value3"); + + // find entries just added, in V3 the findEntries return value is just List + List entries = database.findEntries("prop"); + } + /** * Load KDBX */ @@ -129,7 +157,7 @@ public void splice(Group newParent, Group groupToSplice) { * Group by title */ public Group groupByTitle(Database database) { - List entries = + List entries = database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); // create a new group using DB factory method Group newParent = database.newGroup("Found entries"); diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index 9c5618b0..91c54480 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -18,10 +18,8 @@ package org.linguafranca.pwdb.kdb; import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractEntry; import org.linguafranca.pwdb.base.AbstractGroup; import org.linguafranca.pwdb.Entry; @@ -41,8 +39,8 @@ public class KdbGroup extends AbstractGroup { private UUID uuid = UUID.randomUUID(); private String name = ""; private Icon icon = new KdbIcon(0); - private List groups = new ArrayList<>(); - private List entries = new ArrayList<>(); + protected List groups = new ArrayList<>(); + protected List entries = new ArrayList<>(); private Date creationTime; private Date lastModificationTime; private Date lastAccessTime; @@ -76,7 +74,7 @@ public KdbGroup removeGroup(Group g) { } @Override - public List getEntries() { + public List getEntries() { return new ArrayList<>(entries); } @@ -174,7 +172,7 @@ public void setIcon(Icon icon) { } @Override - public List getGroups() { + public List getGroups() { return new ArrayList<>(groups); } diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java index b31ef3e5..07afd79c 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java @@ -106,8 +106,8 @@ public static KdbDatabase createKdbDatabase(Credentials credentials, KdbHeader k } private static void setDatabase(KdbDatabase kdbDatabase, KdbGroup group) { - for (KdbGroup child: group.getGroups()){ - ((KdbGroup) child).database = kdbDatabase; + for (KdbGroup child: group.groups){ + child.database = kdbDatabase; setDatabase(kdbDatabase, child); } } diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 5024f654..33113775 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -182,7 +182,7 @@ public void setParent(Group g) { } @Override - public List getGroups() { + public List getGroups() { return new ArrayList<>(groups); } @@ -223,7 +223,7 @@ public KdbxGroup removeGroup(Group g) { } @Override - public List getEntries() { + public List getEntries() { return new ArrayList<>(this.entries); } From ed4f21ac81986035414ab9ef61f468b44f274d77 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 10 Mar 2025 13:01:53 +0000 Subject: [PATCH 36/66] change base to abstractdb package --- .../pwdb/{base => abstractdb}/AbstractDatabase.java | 2 +- .../linguafranca/pwdb/{base => abstractdb}/AbstractEntry.java | 2 +- .../linguafranca/pwdb/{base => abstractdb}/AbstractGroup.java | 2 +- .../java/org/linguafranca/pwdb/protect/ProtectedDatabase.java | 2 +- kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java | 2 +- kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java | 2 +- kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java | 2 +- .../main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java | 2 +- .../main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename database/src/main/java/org/linguafranca/pwdb/{base => abstractdb}/AbstractDatabase.java (99%) rename database/src/main/java/org/linguafranca/pwdb/{base => abstractdb}/AbstractEntry.java (99%) rename database/src/main/java/org/linguafranca/pwdb/{base => abstractdb}/AbstractGroup.java (98%) diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java similarity index 99% rename from database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java rename to database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java index e873d723..f679bab3 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.base; +package org.linguafranca.pwdb.abstractdb; import org.linguafranca.pwdb.*; diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractEntry.java similarity index 99% rename from database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java rename to database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractEntry.java index 6cae57ec..145617f1 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractEntry.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.base; +package org.linguafranca.pwdb.abstractdb; import org.linguafranca.pwdb.*; diff --git a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractGroup.java similarity index 98% rename from database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java rename to database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractGroup.java index 227208f6..6a4c5f48 100644 --- a/database/src/main/java/org/linguafranca/pwdb/base/AbstractGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractGroup.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.base; +package org.linguafranca.pwdb.abstractdb; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index 2da5cda0..5e98092f 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -18,7 +18,7 @@ package org.linguafranca.pwdb.protect; import org.linguafranca.pwdb.*; -import org.linguafranca.pwdb.base.AbstractDatabase; +import org.linguafranca.pwdb.abstractdb.AbstractDatabase; import java.util.Collections; import java.util.List; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java index b6682a82..11c2575b 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java @@ -18,7 +18,7 @@ package org.linguafranca.pwdb.kdb; import org.linguafranca.pwdb.*; -import org.linguafranca.pwdb.base.AbstractDatabase; +import org.linguafranca.pwdb.abstractdb.AbstractDatabase; import java.io.IOException; import java.io.InputStream; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index 78fb9eff..0e7b22eb 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -21,7 +21,7 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractEntry; +import org.linguafranca.pwdb.abstractdb.AbstractEntry; import java.util.ArrayList; import java.util.Date; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index 91c54480..740bd58c 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -20,7 +20,7 @@ import org.jetbrains.annotations.NotNull; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractGroup; +import org.linguafranca.pwdb.abstractdb.AbstractGroup; import org.linguafranca.pwdb.Entry; import java.util.ArrayList; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index d0ba27db..f9427f3b 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -27,7 +27,7 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.PropertyValue; -import org.linguafranca.pwdb.base.AbstractEntry; +import org.linguafranca.pwdb.abstractdb.AbstractEntry; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 33113775..d4f9ea9e 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -28,7 +28,7 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.base.AbstractGroup; +import org.linguafranca.pwdb.abstractdb.AbstractGroup; import org.linguafranca.pwdb.kdbx.jackson.converter.Base64ToUUIDConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; import org.linguafranca.pwdb.kdbx.jackson.converter.StringToBooleanConverter; From 159cf684df5aa7c63193a2f75264aa4dc7eaa60e Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 10 Mar 2025 15:51:47 +0000 Subject: [PATCH 37/66] introducing BasicDatabase and various tidies --- database/pom.xml | 16 ++ .../java/org/linguafranca/pwdb/Group.java | 6 +- .../pwdb/basic/BasicDatabase.java | 92 +++++++++- .../linguafranca/pwdb/basic/BasicEntry.java | 166 ++++++++++++++++++ .../linguafranca/pwdb/basic/BasicGroup.java | 6 +- .../linguafranca/pwdb/basic/BasicIcon.java | 41 +++++ .../pwdb/basic/BasicDatabaseTest.java | 101 +++++++++++ .../pwdb/basic/BasicEntryTest.java | 161 +++++++++++++++++ .../pwdb/basic/BasicGroupTest.java | 119 +++++++++++++ .../pwdb/protect/ProtectedDatabaseTest.java | 84 +-------- example/pom.xml | 6 + .../SecureCharBufferPropertyValueTest.java | 2 +- .../org/linguafranca/pwdb/kdb/KdbGroup.java | 3 - .../pwdb/kdbx/jackson/KdbxGroup.java | 1 - 14 files changed, 708 insertions(+), 96 deletions(-) create mode 100644 database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java create mode 100644 database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java create mode 100644 database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java create mode 100644 database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java diff --git a/database/pom.xml b/database/pom.xml index e85192fd..6f833178 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -44,4 +44,20 @@ bcpkix-jdk18on + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/Group.java b/database/src/main/java/org/linguafranca/pwdb/Group.java index 6dee7d9a..dae9cf23 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Group.java +++ b/database/src/main/java/org/linguafranca/pwdb/Group.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb; -import org.jetbrains.annotations.NotNull; - import javax.annotation.Nullable; import java.util.List; import java.util.UUID; @@ -235,7 +233,7 @@ public interface Group { void setIcon(Icon icon); /** - * Get the database this group was created from + * Get the database this group is part of, null if the group is not part of a database */ - @NotNull Database getDatabase(); + Database getDatabase(); } diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 7f416bb5..53a7d3ee 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -17,8 +17,96 @@ package org.linguafranca.pwdb.basic; +import org.jetbrains.annotations.Nullable; +import org.linguafranca.pwdb.*; import org.linguafranca.pwdb.protect.ProtectedDatabase; -public class BasicDatabase implements ProtectedDatabase { - // please implement a basic database here +import java.io.IOException; +import java.io.OutputStream; + +public class BasicDatabase extends ProtectedDatabase { + private final Group root; + private String databaseName; + private String databaseDescription; + + public BasicDatabase() { + root = new BasicGroup(this); + root.setName("Root"); + } + + @Override + public Group getRootGroup() { + return root; + } + + @Override + public Group newGroup() { + return new BasicGroup(this); + } + + @Override + public Entry newEntry() { + return new BasicEntry(this); + } + + @Override + public Icon newIcon() { + return new BasicIcon(); + } + + @Override + public Icon newIcon(Integer i) { + return new BasicIcon(i); + } + + @Override + public boolean isRecycleBinEnabled() { + return false; + } + + @Override + public void enableRecycleBin(boolean enable) { + throw new UnsupportedOperationException(); + } + + @Override + public @Nullable Group getRecycleBin() { + return null; + } + + @Override + public String getName() { + return databaseName; + } + + @Override + public void setName(String name) { + this.databaseName = name; + } + + @Override + public String getDescription() { + return databaseDescription; + } + + @Override + public void setDescription(String description) { + this.databaseDescription = description; + + } + + @Override + public void save(Credentials credentials, OutputStream outputStream) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void save(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public StreamFormat getStreamFormat() { + throw new UnsupportedOperationException(); + } } diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java index d3f2f44f..3bed09e9 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java @@ -17,7 +17,173 @@ package org.linguafranca.pwdb.basic; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; +import org.linguafranca.pwdb.Icon; +import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.abstractdb.AbstractEntry; +import java.time.Instant; +import java.util.*; + public class BasicEntry extends AbstractEntry { + private final BasicDatabase database; + private final UUID uuid; + private final Map properties = new HashMap<>(); + private final Map binaries = new HashMap<>(); + private final Instant creationTime; + private Group parent; + private Icon icon; + private Instant expiryTime; + private Instant accessTime; + private Instant modifiedTime; + private boolean isExpires; + + + public BasicEntry(BasicDatabase basicDatabase) { + this.database = basicDatabase; + this.uuid = UUID.randomUUID(); + this.creationTime = Instant.now(); + } + + @Override + protected void touch() { + database.setDirty(true); + } + + @Override + public String getProperty(String name) { + if (!properties.containsKey(name)) { + return null; + } + updateAccessTime(); + return properties.get(name).getValueAsString(); + } + + @Override + public Entry setProperty(String name, String value) { + updateModifiedTime(); + properties.put(name, PropertyValue.StringStore.getFactory().of(value)); + return this; + } + + @Override + public boolean removeProperty(String name) throws IllegalArgumentException, UnsupportedOperationException { + boolean exists = properties.containsKey(name); + if (exists) { + updateModifiedTime(); + } + properties.remove(name); + return exists; + } + + @Override + public List getPropertyNames() { + return List.of(properties.keySet().toArray(String[]::new)); + } + + @Override + public byte[] getBinaryProperty(String name) { + if (!binaries.containsKey(name)) { + return null; + } + updateAccessTime(); + return binaries.get(name).getValueAsBytes(); + } + + @Override + public void setBinaryProperty(String name, byte[] value) { + updateModifiedTime(); + binaries.put(name, PropertyValue.BytesStore.getFactory().of(value)); + } + + @Override + public boolean removeBinaryProperty(String name) throws UnsupportedOperationException { + boolean exists = binaries.containsKey(name); + if (exists) {updateModifiedTime();} + binaries.remove(name); + return exists; + } + + @Override + public List getBinaryPropertyNames() { + return List.of(binaries.keySet().toArray(String[]::new)); + } + + @Override + public @Nullable Group getParent() { + return parent; + } + + @Override + public @NotNull UUID getUuid() { + return uuid; + } + + @Override + public Icon getIcon() { + return icon; + } + + @Override + public void setIcon(Icon icon) { + this.icon = icon; + } + + @Override + public Date getLastAccessTime() { + if (accessTime == null) { + return fromInstant(this.creationTime); + } + return fromInstant(this.accessTime); + } + + @Override + public Date getCreationTime() { + return fromInstant(creationTime); + } + + @Override + public boolean getExpires() { + return this.isExpires; + } + + @Override + public void setExpires(boolean expires) { + this.isExpires = expires; + } + + @Override + public Date getExpiryTime() { + return fromInstant(this.expiryTime); + } + + @Override + public void setExpiryTime(Date expiryTime) throws IllegalArgumentException { + this.expiryTime = expiryTime.toInstant(); + } + + @Override + public Date getLastModificationTime() { + return fromInstant(this.modifiedTime); + } + + private void updateAccessTime() { + accessTime = Instant.now(); + touch(); + } + + private void updateModifiedTime() { + modifiedTime = Instant.now(); + touch(); + } + + private Date fromInstant(Instant instant) { + if (instant == null) { + return null; + } + return Date.from(instant); + } } diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java index b394633d..9f61f9de 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java @@ -70,7 +70,7 @@ public void setParent(Group parent) { } @Override - public List getGroups() { + public List getGroups() { return Collections.unmodifiableList(groups); } @@ -92,7 +92,7 @@ public Group removeGroup(Group group) { } @Override - public List getEntries() { + public List getEntries() { return Collections.unmodifiableList(entries); } @@ -138,7 +138,7 @@ public void setIcon(Icon icon) { } @Override - public @NotNull Database getDatabase() { + public Database getDatabase() { return this.database; } } diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java new file mode 100644 index 00000000..7a1d7830 --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.linguafranca.pwdb.Icon; + +public class BasicIcon implements Icon { + private int index; + + public BasicIcon() { + } + + public BasicIcon(Integer i) { + this.index = i; + } + + @Override + public int getIndex() { + return index; + } + + @Override + public void setIndex(int index) { + this.index = index; + } +} diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java new file mode 100644 index 00000000..acb67d28 --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; +import org.linguafranca.pwdb.Icon; + +import static org.junit.jupiter.api.Assertions.*; + +class BasicDatabaseTest { + + private BasicDatabase database; + + @BeforeEach + void setUp() { + database = new BasicDatabase(); + } + + @Test + void testGetRootGroup() { + Group root = database.getRootGroup(); + assertNotNull(root); + assertEquals("Root", root.getName()); + } + + @Test + void testNewGroup() { + Group group = database.newGroup(); + assertNotNull(group); + } + + @Test + void testNewEntry() { + Entry entry = database.newEntry(); + assertNotNull(entry); + } + + @Test + void testNewIcon() { + Icon icon = database.newIcon(); + assertNotNull(icon); + } + + @Test + void testNewIconWithInteger() { + Icon icon = database.newIcon(1); + assertNotNull(icon); + } + + @Test + void testIsRecycleBinEnabled() { + assertFalse(database.isRecycleBinEnabled()); + } + + @Test + void testEnableRecycleBin() { + assertThrows(UnsupportedOperationException.class, () -> database.enableRecycleBin(true)); + } + + @Test + void testGetRecycleBin() { + assertNull(database.getRecycleBin()); + } + + @Test + void testGetName() { + assertNull(database.getName()); + } + + @Test + void testGetSetDescription() { + assertNull(database.getDescription()); + database.setName("Test Database"); + assertEquals("Test Database", database.getName()); + } + + @Test + void testSetDescription() { + database.setDescription("Test Description"); + assertEquals("Test Description", database.getDescription()); + } +} diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java b/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java new file mode 100644 index 00000000..aa9656d8 --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class BasicEntryTest { + + private BasicEntry entry; + + @BeforeEach + void setUp() { + entry = new BasicEntry(new BasicDatabase()); + } + + @Test + void testGetProperty() { + entry.setProperty("name", "value"); + assertEquals("value", entry.getProperty("name")); + } + + @Test + void testSetProperty() { + entry.setProperty("name", "value"); + assertEquals("value", entry.getProperty("name")); + } + + @Test + void testRemoveProperty() { + entry.setProperty("name", "value"); + assertTrue(entry.removeProperty("name")); + assertNull(entry.getProperty("name")); + } + + @Test + void testGetPropertyNames() { + entry.setProperty("name1", "value1"); + entry.setProperty("name2", "value2"); + List propertyNames = entry.getPropertyNames(); + assertTrue(propertyNames.contains("name1")); + assertTrue(propertyNames.contains("name2")); + } + + @Test + void testGetBinaryProperty() { + byte[] data = {1, 2, 3}; + entry.setBinaryProperty("binary", data); + assertArrayEquals(data, entry.getBinaryProperty("binary")); + } + + @Test + void testSetBinaryProperty() { + byte[] data = {1, 2, 3}; + entry.setBinaryProperty("binary", data); + assertArrayEquals(data, entry.getBinaryProperty("binary")); + } + + @Test + void testRemoveBinaryProperty() { + byte[] data = {1, 2, 3}; + entry.setBinaryProperty("binary", data); + assertTrue(entry.removeBinaryProperty("binary")); + assertNull(entry.getBinaryProperty("binary")); + } + + @Test + void testGetBinaryPropertyNames() { + byte[] data1 = {1, 2, 3}; + byte[] data2 = {4, 5, 6}; + entry.setBinaryProperty("binary1", data1); + entry.setBinaryProperty("binary2", data2); + List binaryPropertyNames = entry.getBinaryPropertyNames(); + assertTrue(binaryPropertyNames.contains("binary1")); + assertTrue(binaryPropertyNames.contains("binary2")); + } + + @Test + void testGetParent() { + assertNull(entry.getParent()); + } + + @Test + void testGetUuid() { + UUID uuid = entry.getUuid(); + assertNotNull(uuid); + } + + @Test + void testGetIcon() { + assertNull(entry.getIcon()); + } + + @Test + void testSetIcon() { + entry.setIcon(new BasicIcon() {}); + assertNotNull(entry.getIcon()); + } + + @Test + void testGetLastAccessTime() { + Date lastAccessTime = entry.getLastAccessTime(); + assertNotNull(lastAccessTime); + } + + @Test + void testGetCreationTime() { + Date creationTime = entry.getCreationTime(); + assertNotNull(creationTime); + } + + @Test + void testGetExpires() { + assertFalse(entry.getExpires()); + } + + @Test + void testSetExpires() { + entry.setExpires(true); + assertTrue(entry.getExpires()); + } + + @Test + void testGetExpiryTime() { + assertNull(entry.getExpiryTime()); + } + + @Test + void testSetExpiryTime() { + Date expiryTime = Date.from(Instant.now()); + entry.setExpiryTime(expiryTime); + assertEquals(expiryTime, entry.getExpiryTime()); + } + + @Test + void testGetLastModificationTime() { + assertNull(entry.getLastModificationTime()); + } +} diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java b/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java new file mode 100644 index 00000000..135d5ff3 --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; + +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; + +class BasicGroupTest { + + private BasicGroup group; + + @BeforeEach + void setUp() { + group = new BasicGroup(new BasicDatabase()); + } + + @Test + void testGetName() { + group.setName("Test Group"); + assertEquals("Test Group", group.getName()); + } + + @Test + void testSetName() { + group.setName("Test Group"); + assertEquals("Test Group", group.getName()); + } + + @Test + void testGetEntries() { + Entry entry = new BasicEntry(new BasicDatabase()); + group.addEntry(entry); + List entries = group.getEntries(); + assertTrue(entries.contains(entry)); + } + + @Test + void testAddEntry() { + Entry entry = new BasicEntry(new BasicDatabase()); + group.addEntry(entry); + assertTrue(group.getEntries().contains(entry)); + } + + @Test + void testRemoveEntry() { + Entry entry = new BasicEntry(new BasicDatabase()); + group.addEntry(entry); + group.removeEntry(entry); + assertFalse(group.getEntries().contains(entry)); + } + + @Test + void testGetGroups() { + Group subGroup = new BasicGroup(new BasicDatabase()); + group.addGroup(subGroup); + List groups = group.getGroups(); + assertTrue(groups.contains(subGroup)); + } + + @Test + void testAddGroup() { + Group subGroup = new BasicGroup(new BasicDatabase()); + group.addGroup(subGroup); + assertTrue(group.getGroups().contains(subGroup)); + } + + @Test + void testRemoveGroup() { + Group subGroup = new BasicGroup(new BasicDatabase()); + group.addGroup(subGroup); + group.removeGroup(subGroup); + assertFalse(group.getGroups().contains(subGroup)); + } + + @Test + void testGetParent() { + assertNull(group.getParent()); + } + + @Test + void testGetUuid() { + UUID uuid = group.getUuid(); + assertNotNull(uuid); + } + + @Test + void testGetIcon() { + assertNull(group.getIcon()); + } + + @Test + void testSetIcon() { + group.setIcon(new BasicIcon() { + }); + assertNotNull(group.getIcon()); + } +} \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java index 0e640d4e..2347006e 100644 --- a/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java @@ -17,14 +17,10 @@ package org.linguafranca.pwdb.protect; -import org.jetbrains.annotations.Nullable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.*; -import org.linguafranca.pwdb.protect.ProtectedDatabase; - -import java.io.IOException; -import java.io.OutputStream; +import org.linguafranca.pwdb.basic.BasicDatabase; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -35,83 +31,7 @@ class ProtectedDatabaseTest { @BeforeEach void setUp() { - database = new ProtectedDatabase() { - @Override - public Group getRootGroup() { - return null; - } - - @Override - public Group newGroup() { - return null; - } - - @Override - public Entry newEntry() { - return null; - } - - @Override - public Icon newIcon() { - return null; - } - - @Override - public Icon newIcon(Integer i) { - return null; - } - - @Override - public boolean isRecycleBinEnabled() { - return false; - } - - @Override - public void enableRecycleBin(boolean enable) { - - } - - @Override - public @Nullable Group getRecycleBin() { - return null; - } - - @Override - public String getName() { - return ""; - } - - @Override - public void setName(String name) { - - } - - @Override - public String getDescription() { - return ""; - } - - @Override - public void setDescription(String description) { - - } - - @Override - public void save(Credentials credentials, OutputStream outputStream) throws IOException { - - } - - @Override - public void save(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) throws IOException { - - } - - @Override - public StreamFormat getStreamFormat() { - return null; - } - // Implement abstract methods if necessary for testing - }; + database = new BasicDatabase(); } @Test diff --git a/example/pom.xml b/example/pom.xml index 02ea27e9..97587d27 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -58,6 +58,12 @@ ${project.version} test-jar + + org.linguafranca.pwdb + database + ${project.version} + test-jar + diff --git a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java index aa6eec90..f9cdfb3b 100644 --- a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java @@ -19,7 +19,7 @@ import org.junit.Test; -import static org.linguafranca.pwdb.util.PropertyValueUtil.genericTest; +import static org.linguafranca.pwdb.PropertyValueUtil.genericTest; public class SecureCharBufferPropertyValueTest { diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java index 0f54b225..46ae7c81 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbGroup.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb.kdb; -import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.abstractdb.AbstractGroup; @@ -215,7 +213,6 @@ public String toString() { return getPath() + String.format(" (%s) %s [%d]", uuid.toString(), time, flags); } - @NotNull public KdbDatabase getDatabase() { return database; } diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index d4f9ea9e..7721c933 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -286,7 +286,6 @@ public void setIcon(Icon icon) { touch(); } - @NotNull @Override public KdbxDatabase getDatabase() { return database; From e27552efed85cf70e0a11d3735971ba0a6a50718 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 11 Mar 2025 09:56:34 +0000 Subject: [PATCH 38/66] major test refactor --- .../org/linguafranca/pwdb/PropertyValue.java | 3 +- .../pwdb/abstractdb/AbstractDatabase.java | 2 +- .../pwdb/basic/BasicDatabase.java | 1 + .../linguafranca/pwdb/basic/BasicEntry.java | 16 +- .../linguafranca/pwdb/basic/BasicGroup.java | 19 +- .../linguafranca/pwdb/basic/BasicIcon.java | 14 + .../pwdb/basic/BasicDatabaseTest.java | 100 +-- .../pwdb/basic/BasicEntryTest.java | 2 +- .../pwdb/basic/BasicGroupTest.java | 12 +- .../pwdb/protect/ProtectedDatabaseTest.java | 83 --- .../pwdb/test/CrudDatabaseTest.java | 277 ++++++++ .../pwdb/test/ProtectedPropertyTest.java | 100 +++ .../pwdb/test/RecycleBinTest.java | 107 +++ .../pwdb/test/TrivialDatabaseTest.java | 167 +++++ .../pwdb/kdbx/jackson/KdbxDatabase.java | 5 + pom.xml | 643 +++++++++--------- util/pom.xml | 9 +- 17 files changed, 1065 insertions(+), 495 deletions(-) delete mode 100644 database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java create mode 100644 database/src/test/java/org/linguafranca/pwdb/test/CrudDatabaseTest.java create mode 100644 database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java create mode 100644 database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java create mode 100644 database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java diff --git a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java index f1073165..66dc3d87 100644 --- a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java +++ b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java @@ -125,8 +125,7 @@ class Default implements Strategy { @Override public List getProtectedProperties() { - //noinspection ArraysAsListWithZeroOrOneArgument - return new ArrayList<>(Arrays.asList(Entry.STANDARD_PROPERTY_NAME_PASSWORD)); + return List.of(Entry.STANDARD_PROPERTY_NAME_PASSWORD); } @Override diff --git a/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java index f679bab3..1ebb319a 100644 --- a/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java @@ -232,7 +232,7 @@ public boolean supportsBinaryProperties() { @Override public boolean supportsRecycleBin() { - return true; + return false; } @Override diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 53a7d3ee..66f92ae7 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.util.UUID; public class BasicDatabase extends ProtectedDatabase { private final Group root; diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java index 3bed09e9..e8d812d2 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java @@ -34,7 +34,7 @@ public class BasicEntry extends AbstractEntry { private final Map properties = new HashMap<>(); private final Map binaries = new HashMap<>(); private final Instant creationTime; - private Group parent; + protected Group parent; private Icon icon; private Instant expiryTime; private Instant accessTime; @@ -46,6 +46,9 @@ public BasicEntry(BasicDatabase basicDatabase) { this.database = basicDatabase; this.uuid = UUID.randomUUID(); this.creationTime = Instant.now(); + for (String name : Entry.STANDARD_PROPERTY_NAMES) { + properties.put(name, new PropertyValue.StringStore("")); + } } @Override @@ -71,6 +74,12 @@ public Entry setProperty(String name, String value) { @Override public boolean removeProperty(String name) throws IllegalArgumentException, UnsupportedOperationException { + if (Entry.STANDARD_PROPERTY_NAMES.contains(name)) { + throw new IllegalArgumentException("Cannot remove standard property"); + } + if (!database.supportsNonStandardPropertyNames()) { + throw new UnsupportedOperationException("Database does not support non-standard properties"); + } boolean exists = properties.containsKey(name); if (exists) { updateModifiedTime(); @@ -81,7 +90,7 @@ public boolean removeProperty(String name) throws IllegalArgumentException, Unsu @Override public List getPropertyNames() { - return List.of(properties.keySet().toArray(String[]::new)); + return new ArrayList<>(properties.keySet()); } @Override @@ -167,6 +176,9 @@ public void setExpiryTime(Date expiryTime) throws IllegalArgumentException { @Override public Date getLastModificationTime() { + if (modifiedTime == null) { + return fromInstant(this.creationTime); + } return fromInstant(this.modifiedTime); } diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java index 9f61f9de..6259a8c4 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java @@ -42,6 +42,8 @@ public class BasicGroup extends AbstractGroup { BasicGroup(@NotNull BasicDatabase database) { this.database = database; this.uuid = UUID.randomUUID(); + this.icon = new BasicIcon(); + this.name = ""; } BasicGroup(@NotNull BasicDatabase database, String name) { @@ -71,7 +73,7 @@ public void setParent(Group parent) { @Override public List getGroups() { - return Collections.unmodifiableList(groups); + return new ArrayList<>(groups); } @Override @@ -81,13 +83,17 @@ public int getGroupsCount() { @Override public Group addGroup(Group group) { - groups.add((BasicGroup) group); + BasicGroup basicGroup = (BasicGroup) group; + groups.add(basicGroup); + basicGroup.parent = this; return group; } @Override public Group removeGroup(Group group) { - groups.remove((BasicGroup) group); + BasicGroup basicGroup = (BasicGroup) group; + groups.remove(basicGroup); + basicGroup.parent = null; return group; } @@ -103,12 +109,17 @@ public int getEntriesCount() { @Override public Entry addEntry(Entry entry) { - entries.add((BasicEntry) entry); + BasicEntry basicEntry = (BasicEntry) entry; + entries.add(basicEntry); + basicEntry.parent = this; return entry; } @Override public Entry removeEntry(Entry entry) { + BasicEntry basicEntry = (BasicEntry) entry; + entries.remove(basicEntry); + (basicEntry).parent = null; return entry; } diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java index 7a1d7830..19006b47 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java @@ -19,6 +19,8 @@ import org.linguafranca.pwdb.Icon; +import java.util.Objects; + public class BasicIcon implements Icon { private int index; @@ -38,4 +40,16 @@ public int getIndex() { public void setIndex(int index) { this.index = index; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BasicIcon)) return false; + BasicIcon basicIcon = (BasicIcon) o; + return index == basicIcon.index; + } + + @Override + public int hashCode() { + return Objects.hashCode(index); + } } diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java index acb67d28..e51d7d87 100644 --- a/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java @@ -17,85 +17,43 @@ package org.linguafranca.pwdb.basic; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.Icon; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.test.CrudDatabaseTest; +import org.linguafranca.pwdb.test.RecycleBinTest; +import org.linguafranca.pwdb.test.ProtectedPropertyTest; +import org.linguafranca.pwdb.test.TrivialDatabaseTest; -import static org.junit.jupiter.api.Assertions.*; +public class BasicDatabaseTest implements + TrivialDatabaseTest, + CrudDatabaseTest, + RecycleBinTest, + ProtectedPropertyTest { -class BasicDatabaseTest { + Database database; - private BasicDatabase database; - - @BeforeEach - void setUp() { - database = new BasicDatabase(); + /** + * Create a new database + */ + @Override + public Database createDatabase() { + return new BasicDatabase(); } - @Test - void testGetRootGroup() { - Group root = database.getRootGroup(); - assertNotNull(root); - assertEquals("Root", root.getName()); + /** + * Create a new database for default use in tests + */ + @Override + public void newDatabase() { + database = createDatabase(); } - @Test - void testNewGroup() { - Group group = database.newGroup(); - assertNotNull(group); + /** + * Get the current default database + */ + @Override + public Database getDatabase() { + return database; } - @Test - void testNewEntry() { - Entry entry = database.newEntry(); - assertNotNull(entry); - } - @Test - void testNewIcon() { - Icon icon = database.newIcon(); - assertNotNull(icon); - } - - @Test - void testNewIconWithInteger() { - Icon icon = database.newIcon(1); - assertNotNull(icon); - } - - @Test - void testIsRecycleBinEnabled() { - assertFalse(database.isRecycleBinEnabled()); - } - - @Test - void testEnableRecycleBin() { - assertThrows(UnsupportedOperationException.class, () -> database.enableRecycleBin(true)); - } - - @Test - void testGetRecycleBin() { - assertNull(database.getRecycleBin()); - } - - @Test - void testGetName() { - assertNull(database.getName()); - } - - @Test - void testGetSetDescription() { - assertNull(database.getDescription()); - database.setName("Test Database"); - assertEquals("Test Database", database.getName()); - } - - @Test - void testSetDescription() { - database.setDescription("Test Description"); - assertEquals("Test Description", database.getDescription()); - } } diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java b/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java index aa9656d8..45f46862 100644 --- a/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java @@ -156,6 +156,6 @@ void testSetExpiryTime() { @Test void testGetLastModificationTime() { - assertNull(entry.getLastModificationTime()); + assertEquals(entry.getCreationTime(), entry.getLastModificationTime()); } } diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java b/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java index 135d5ff3..bf5e3262 100644 --- a/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java @@ -90,7 +90,12 @@ void testAddGroup() { void testRemoveGroup() { Group subGroup = new BasicGroup(new BasicDatabase()); group.addGroup(subGroup); + assertTrue(group.getGroups().contains(subGroup)); + assertEquals(1, group.getGroups().size()); + assertEquals(group, subGroup.getParent()); group.removeGroup(subGroup); + assertEquals(0, group.getGroups().size()); + assertNull(subGroup.getParent()); assertFalse(group.getGroups().contains(subGroup)); } @@ -107,13 +112,12 @@ void testGetUuid() { @Test void testGetIcon() { - assertNull(group.getIcon()); + assertEquals(0, group.getIcon().getIndex()); } @Test void testSetIcon() { - group.setIcon(new BasicIcon() { - }); - assertNotNull(group.getIcon()); + group.setIcon(new BasicIcon(1)); + assertEquals(1, group.getIcon().getIndex()); } } \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java deleted file mode 100644 index 2347006e..00000000 --- a/database/src/test/java/org/linguafranca/pwdb/protect/ProtectedDatabaseTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.protect; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.linguafranca.pwdb.*; -import org.linguafranca.pwdb.basic.BasicDatabase; -import java.util.List; - -import static org.junit.jupiter.api.Assertions.*; - -class ProtectedDatabaseTest { - - private ProtectedDatabase database; - - @BeforeEach - void setUp() { - database = new BasicDatabase(); - } - - @Test - void testShouldProtect() { - String propertyName = "password"; - database.setShouldProtect(propertyName, true); - assertTrue(database.shouldProtect(propertyName)); - } - - @Test - void testSetShouldProtect() { - String propertyName = "password"; - database.setShouldProtect(propertyName, true); - assertTrue(database.shouldProtect(propertyName)); - - database.setShouldProtect(propertyName, false); - assertFalse(database.shouldProtect(propertyName)); - } - - @Test - void testListShouldProtect() { - String propertyName1 = "password"; - String propertyName2 = "username"; - database.setShouldProtect(propertyName1, true); - database.setShouldProtect(propertyName2, true); - - List protectedProperties = database.listShouldProtect(); - assertTrue(protectedProperties.contains(propertyName1)); - assertTrue(protectedProperties.contains(propertyName2)); - } - - @Test - void testGetPropertyValueStrategy() { - PropertyValue.Strategy strategy = database.getPropertyValueStrategy(); - assertNotNull(strategy); - } - - @Test - void testSetPropertyValueStrategy() { - PropertyValue.Strategy newStrategy = new PropertyValue.Strategy.Default(); - database.setPropertyValueStrategy(newStrategy); - assertEquals(newStrategy, database.getPropertyValueStrategy()); - } - - @Test - void testSupportsPropertyValueStrategy() { - assertTrue(database.supportsPropertyValueStrategy()); - } -} \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/test/CrudDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/test/CrudDatabaseTest.java new file mode 100644 index 00000000..3b764b0e --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/test/CrudDatabaseTest.java @@ -0,0 +1,277 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.test;/* + * Copyright 2015 Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; +import org.linguafranca.pwdb.Icon; + +import java.io.IOException; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * @author Jo + */ +public interface CrudDatabaseTest { + void newDatabase(); + Database getDatabase(); + Database createDatabase(); + + @Test + default void testEmptyDatabase() { + assertEquals("Root", getDatabase().getRootGroup().getName()); + assertEquals(0, getDatabase().getRootGroup().getEntries().size()); + assertEquals(0, getDatabase().getRootGroup().getGroups().size()); + } + + @Test + default void testAddGroup() { + Group g1 = getDatabase().getRootGroup().addGroup(getDatabase().newGroup("group1")); + assertEquals(1, getDatabase().getRootGroup().getGroups().size()); + assertEquals("group1", g1.getName()); + assertEquals(0, g1.getGroups().size()); + assertEquals(0, g1.getEntries().size()); + assertEquals(g1.getParent(), getDatabase().getRootGroup(), "root is not the parent of its child"); + + Group g2 = getDatabase().newGroup(); + assertEquals("", g2.getName()); + assertNotNull(g2.getUuid()); + assertEquals(0, g2.getIcon().getIndex()); + assertEquals(0, g2.getGroups().size()); + assertEquals(0, g2.getEntries().size()); + + // show that the list of groups is a copy + getDatabase().getRootGroup().getGroups().clear(); + assertEquals(1, getDatabase().getRootGroup().getGroups().size()); + assertEquals(0, g1.getEntries().size()); + } + + @Test + default void testDeleteGroupDetail () { + Group g1 = getDatabase().getRootGroup().addGroup(getDatabase().newGroup("group1")); + List l1 = getDatabase().getRootGroup().findGroups("group1"); + assertEquals(1, l1.size()); + Group g2 = l1.get(0); + assertEquals(g2, g1); + Group g3 = getDatabase().getRootGroup().removeGroup(g2); + assertEquals(g3, g1); + assertNull(g1.getParent()); + assertEquals(0, getDatabase().getRootGroup().getGroups().size()); + assertEquals(0, getDatabase().getRootGroup().findGroups("group1").size()); + } + + @Test + default void testAddRemoveEntry() { + Entry e1 = getDatabase().getRootGroup().addEntry(getDatabase().newEntry()); + e1.setTitle("entry1"); + List l1 = getDatabase().findEntries("entry1"); + assertEquals(1, l1.size()); + + Entry e12 = getDatabase().getRootGroup().addEntry(getDatabase().newEntry("entry12")); + List l2 = getDatabase().findEntries("entry1"); + assertEquals(2, l2.size()); + + // show that the entries are different + assertNotEquals(l2.get(0), l2.get(1)); + + // show that the list is a copy + l2.clear(); + assertEquals(2, getDatabase().findEntries("entry1").size()); + + // show that we get an equivalent entry when we remove to when we inserted + Entry e12b = getDatabase().getRootGroup().removeEntry(e12); + assertEquals(e12b, e12); + // has been unhooked from parent + assertNull(e12.getParent()); + assertEquals(1, getDatabase().findEntries("entry1").size()); + } + + @Test + default void testSetFields () { + Entry e1 = getDatabase().newEntry("Entry 1"); + e1.setNotes("this looks a little like Entry 2"); + assertEquals("this looks a little like Entry 2", e1.getNotes()); + e1.setUsername("jake@window.com"); + assertEquals("jake@window.com", e1.getUsername()); + e1.setPassword("supercalifragelisticexpialidocious"); + assertEquals("supercalifragelisticexpialidocious", e1.getPassword()); + e1.setUrl("https://window.com"); + assertEquals("https://window.com", e1.getUrl()); + + + assertTrue(e1.match("2")); + assertTrue(e1.matchTitle("1")); + assertFalse(e1.matchTitle("doggy")); + + Icon ic1 = getDatabase().newIcon(27); + e1.setIcon(ic1); + assertEquals(e1.getIcon(), ic1); + + // databases have to support setting of standard properties + e1.setProperty(Entry.STANDARD_PROPERTY_NAME_TITLE, "A title"); + assertEquals("A title", e1.getTitle()); + e1.setProperty(Entry.STANDARD_PROPERTY_NAME_USER_NAME, "username"); + assertEquals("username", e1.getUsername()); + e1.setProperty(Entry.STANDARD_PROPERTY_NAME_NOTES, "notes"); + assertEquals("notes", e1.getNotes()); + e1.setProperty(Entry.STANDARD_PROPERTY_NAME_PASSWORD, "password"); + assertEquals("password", e1.getPassword()); + e1.setProperty(Entry.STANDARD_PROPERTY_NAME_URL, "url"); + assertEquals("url", e1.getUrl()); + + try { + e1.setProperty("silly", "hello"); + assertEquals("hello", e1.getProperty("silly")); + List properties = new ArrayList<>(Entry.STANDARD_PROPERTY_NAMES); + properties.add("silly"); + // remove all properties to show that getProperties returns all the values we want + properties.removeAll(e1.getPropertyNames()); + assertEquals(0, properties.size()); + } catch (UnsupportedOperationException e) { + // databases don't have to support arbitrary properties + assertFalse(getDatabase().supportsNonStandardPropertyNames()); + assertArrayEquals(e1.getPropertyNames().toArray(), Entry.STANDARD_PROPERTY_NAMES.toArray()); + } + + e1.setNotes("How much is that doggy in the window?"); + assertEquals("How much is that doggy in the window?", e1.getNotes()); + e1.setTitle("Entry 2"); + assertEquals("Entry 2", e1.getTitle()); + assertEquals("Entry 2", e1.getPath()); + } + + @Test + default void testTimes() { + long beforeSecond = Instant.now().toEpochMilli()/1000; + Entry entry = getDatabase().newEntry(); + long afterSecond = Instant.now().toEpochMilli()/1000; + long createdSecond = entry.getCreationTime().getTime()/1000; + + assertTrue(createdSecond >= beforeSecond && createdSecond <= afterSecond); + assertFalse(entry.getExpires()); + assertTrue(entry.getLastAccessTime().getTime()/1000 <= createdSecond); + assertTrue(entry.getLastModificationTime().getTime()/1000 <= createdSecond); + + entry.setExpires(true); + entry.setExpiryTime(new Date(createdSecond*1000)); + + assertTrue(entry.getExpires()); + assertEquals(createdSecond, entry.getExpiryTime().getTime()/1000); + + + } + + @Test + default void checkAddChangeRemoveProperty() { + // only applies to databases that support arbitrary properties + Entry entry = getDatabase().newEntry(); + assertEquals(Entry.STANDARD_PROPERTY_NAMES.size(), entry.getPropertyNames().size()); + try { + entry.setProperty("test", "test1"); + } catch (UnsupportedOperationException e) { + if (!getDatabase().supportsNonStandardPropertyNames()) { + return; + } + fail("Database must report that it doesn't support non standard properties"); + } + assertEquals("test1", entry.getProperty("test")); + entry.setProperty("test", "test2"); + assertEquals("test2", entry.getProperty("test")); + assertTrue(entry.removeProperty("test")); + assertFalse(entry.removeProperty("test")); + assertFalse(entry.removeProperty("test-test")); + assertEquals(Entry.STANDARD_PROPERTY_NAMES.size(), entry.getPropertyNames().size()); + try { + entry.removeProperty(Entry.STANDARD_PROPERTY_NAME_USER_NAME); + fail("Should not be able to remove standard property"); + } catch (IllegalArgumentException ignore) { + // ignore as expected + } + } + + + @Test + default void testNewEntryInit() { + Entry e2 = getDatabase().newEntry(); + assertNull(e2.getParent()); + assertEquals("", e2.getPassword()); + assertNotNull(e2.getUuid()); + assertEquals("", e2.getUrl()); + assertEquals("", e2.getNotes()); + assertEquals("", e2.getUsername()); + assertEquals("", e2.getTitle()); + assertNull(e2.getProperty("silly")); + List l = e2.getPropertyNames(); + l.removeAll(Entry.STANDARD_PROPERTY_NAMES); + assertEquals(0, l.size()); + } + + @Test + default void testCopy() throws IOException { + Entry entry1 = getDatabase().newEntry(); + entry1.setTitle("Entry"); + entry1.setUsername("Username"); + entry1.setPassword("Password"); + entry1.setUrl("https://dont.follow.me"); + entry1.setNotes("Notes"); + entry1.setIcon(getDatabase().newIcon(2)); + + // create a new Database + Database database2 = createDatabase(); + // create a new Entry in new Database + Entry entry2 = database2.newEntry(entry1); + + assertEquals(entry1.getTitle(), entry2.getTitle()); + assertEquals(entry1.getUsername(), entry2.getUsername()); + assertEquals(entry1.getPassword(), entry2.getPassword()); + assertEquals(entry1.getUrl(), entry2.getUrl()); + assertEquals(entry1.getNotes(), entry2.getNotes()); + assertEquals(entry1.getIcon(), entry2.getIcon()); + assertNotEquals(entry1.getUuid(), entry2.getUuid()); + + Group group1 = getDatabase().newGroup(); + group1.setName("Group"); + group1.setIcon(getDatabase().newIcon(3)); + + Group group2 = database2.newGroup(group1); + assertEquals(group1.getName(), group2.getName()); + assertEquals(group1.getIcon(), group2.getIcon()); + assertNotEquals(group1.getUuid(), group2.getUuid()); + } +} diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java new file mode 100644 index 00000000..617d8797 --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.*; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; +import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.PASSWORD; +import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.USER_NAME; + +public interface ProtectedPropertyTest { + + class MutableProtectionStrategy extends PropertyValue.Strategy.Default { + List protectedProperties = new ArrayList<>(); + @Override + public List getProtectedProperties() { + return protectedProperties; + } + } + void newDatabase(); + + Database getDatabase(); + + @BeforeEach + default void setup() throws Exception { + newDatabase(); + } + + @Test + default void testShouldProtect() { + String propertyName = PASSWORD; + assertThrows(UnsupportedOperationException.class, + () -> getDatabase().setShouldProtect(propertyName, true), + "By default the ProtectionStrategy should be immutable"); + assertTrue(getDatabase().shouldProtect(propertyName)); + } + + @Test + default void testSetShouldProtect() { + getDatabase().setPropertyValueStrategy(new MutableProtectionStrategy()); + String propertyName = PASSWORD; + getDatabase().setShouldProtect(propertyName, true); + assertTrue(getDatabase().shouldProtect(propertyName)); + + getDatabase().setShouldProtect(propertyName, false); + assertFalse(getDatabase().shouldProtect(propertyName)); + } + + @Test + default void testListShouldProtect() { + getDatabase().setPropertyValueStrategy(new MutableProtectionStrategy()); + String propertyName1 = PASSWORD; + String propertyName2 = USER_NAME; + getDatabase().setShouldProtect(propertyName1, true); + getDatabase().setShouldProtect(propertyName2, true); + + List protectedProperties = getDatabase().listShouldProtect(); + assertTrue(protectedProperties.contains(propertyName1)); + assertTrue(protectedProperties.contains(propertyName2)); + } + + @Test + default void testGetPropertyValueStrategy() { + PropertyValue.Strategy strategy = getDatabase().getPropertyValueStrategy(); + assertNotNull(strategy); + } + + @Test + default void testSetPropertyValueStrategy() { + PropertyValue.Strategy newStrategy = new PropertyValue.Strategy.Default(); + getDatabase().setPropertyValueStrategy(newStrategy); + assertEquals(newStrategy, getDatabase().getPropertyValueStrategy()); + } + + @Test + default void testSupportsPropertyValueStrategy() { + assertTrue(getDatabase().supportsPropertyValueStrategy()); + } +} \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java b/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java new file mode 100644 index 00000000..0249a563 --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +/** + * @author jo + */ +public interface RecycleBinTest { + + void newDatabase(); + Database getDatabase(); + + @BeforeEach + default void makeDatabase() throws Exception { + newDatabase(); + } + + @Test + default void recycleBinEntries() { + assumeTrue(getDatabase().supportsRecycleBin()); + Database database = getDatabase(); + database.enableRecycleBin(false); + assertFalse(database.isRecycleBinEnabled()); + assertNull(database.getRecycleBin()); + database.enableRecycleBin(true); + assertTrue(database.isRecycleBinEnabled()); + Group recycleBin = database.getRecycleBin(); + assertNotNull(recycleBin); + + Entry entry = database.newEntry(); + assertEquals(entry, database.getRootGroup().addEntry(entry)); + assertEquals(1, database.getRootGroup().getEntriesCount()); + + assertTrue(database.deleteEntry(entry.getUuid())); + assertEquals(0, database.getRootGroup().getEntriesCount()); + assertEquals(1, recycleBin.getEntriesCount()); + + database.enableRecycleBin(false); + assertFalse(database.isRecycleBinEnabled()); + + entry = database.newEntry(); + assertEquals(entry, database.getRootGroup().addEntry(entry)); + assertEquals(1, database.getRootGroup().getEntriesCount()); + + assertTrue(database.deleteEntry(entry.getUuid())); + assertEquals(0, database.getRootGroup().getEntriesCount()); + assertEquals(1, recycleBin.getEntriesCount()); + database.emptyRecycleBin(); + assertEquals(0, recycleBin.getEntriesCount()); + } + + @Test + default void recycleBinGroups() { + assumeTrue(getDatabase().supportsRecycleBin()); + Database database = getDatabase(); + database.enableRecycleBin(true); + assertTrue(database.isRecycleBinEnabled()); + Group recycleBin = database.getRecycleBin(); + assertNotNull(recycleBin); + + Group group= database.newGroup(); + assertEquals(group, database.getRootGroup().addGroup(group)); + assertEquals(2, database.getRootGroup().getGroupsCount()); + + assertTrue(database.deleteGroup(group.getUuid())); + assertEquals(1, database.getRootGroup().getGroupsCount()); + assertEquals(1, recycleBin.getGroupsCount()); + + database.enableRecycleBin(false); + assertFalse(database.isRecycleBinEnabled()); + + group = database.newGroup(); + assertEquals(group, database.getRootGroup().addGroup(group)); + assertEquals(2, database.getRootGroup().getGroupsCount()); + + assertTrue(database.deleteGroup(group.getUuid())); + assertEquals(1, database.getRootGroup().getGroupsCount()); + assertEquals(1, recycleBin.getGroupsCount()); + database.emptyRecycleBin(); + assertEquals(0, recycleBin.getGroupsCount()); + } + +} diff --git a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java new file mode 100644 index 00000000..4b8962e7 --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.test; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; +import org.linguafranca.pwdb.Icon; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +@TestInstance(TestInstance.Lifecycle.PER_CLASS) +public interface TrivialDatabaseTest { + + void newDatabase(); + Database getDatabase(); + + @BeforeEach + default void setUp() { + newDatabase(); + } + + @Test + default void testGetRootGroup() { + Group root = getDatabase().getRootGroup(); + assertNotNull(root); + assertEquals("Root", root.getName()); + } + + @Test + default void testNewGroup() { + Group group = getDatabase().newGroup(); + assertNotNull(group); + } + + @Test + default void testNewEntry() { + Entry entry = getDatabase().newEntry(); + assertNotNull(entry); + } + + @Test + default void testNewIcon() { + Icon icon = getDatabase().newIcon(); + assertNotNull(icon); + } + + @Test + default void testNewIconWithInteger() { + Icon icon = getDatabase().newIcon(1); + assertNotNull(icon); + assertEquals(1, icon.getIndex()); + } + + @Test + default void testSupportsRecycleBin() { + assertFalse(getDatabase().supportsRecycleBin()); + } + + @Test + default void testRecycleBin() { + if (getDatabase().supportsRecycleBin()) { + assertTrue(getDatabase().isRecycleBinEnabled()); + Group recycleBin = getDatabase().getRecycleBin(); + assertNotNull(recycleBin); + } else { + assertFalse(getDatabase().isRecycleBinEnabled()); + assertThrows(UnsupportedOperationException.class, () -> getDatabase().enableRecycleBin(true)); + assertNull(getDatabase().getRecycleBin()); + } + } + + @Test + default void testGetSetName() { + assertNull(getDatabase().getName()); + getDatabase().setName("Test Database"); + assertEquals("Test Database", getDatabase().getName()); + } + + @Test + default void testGetSetDescription() { + assertNull(getDatabase().getDescription()); + getDatabase().setDescription("Test Description"); + assertEquals("Test Description", getDatabase().getDescription()); + } + + @Test + default void testDeleteEntry() { + Entry entry = getDatabase().newEntry(); + getDatabase().getRootGroup().addEntry(entry); + UUID uuid = entry.getUuid(); + assertTrue(getDatabase().deleteEntry(uuid)); + assertNull(getDatabase().findEntry(uuid)); + } + + @Test + default void testDeleteGroup() { + Group group = getDatabase().newGroup(); + getDatabase().getRootGroup().addGroup(group); + UUID uuid = group.getUuid(); + assertTrue(getDatabase().deleteGroup(uuid)); + assertNull(getDatabase().findGroup(uuid)); + } + +/* @Test + default void testSave() throws IOException { + OutputStream outputStream = new OutputStream() { + @Override + public void write(int b) { + // no-op + } + }; + assertThrows(IOException.class, () -> getDatabase().save(null, outputStream)); + }*/ + + @Test + default void testSupportsNonStandardPropertyNames() { + // check standard names are there + Entry entry = getDatabase().newEntry(); + for (String name : Entry.STANDARD_PROPERTY_NAMES) { + assertNotNull(entry.getProperty(name)); + } + // add a random property + if (getDatabase().supportsNonStandardPropertyNames()) { + entry.setProperty("random", "you never know"); + assertEquals("you never know", entry.getProperty("random")); + } else { + assertThrows(UnsupportedOperationException.class, () -> entry.setProperty("random", "you never know")); + } + } + + @Test + default void testSupportsBinaryProperties() { + if (getDatabase().supportsBinaryProperties()) { + Entry entry = getDatabase().newEntry(); + entry.setBinaryProperty("binary", new byte[]{1, 2, 3}); + assertArrayEquals(new byte[]{1, 2, 3}, entry.getBinaryProperty("binary")); + } else { + Entry entry = getDatabase().newEntry(); + assertThrows(UnsupportedOperationException.class, () -> entry.setBinaryProperty("binary", new byte[]{1, 2, 3})); + } + } +} \ No newline at end of file diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index 98313e63..926e60a9 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -165,6 +165,11 @@ public KdbxGroup getRecycleBin() { return g; } + @Override + public boolean supportsRecycleBin() { + return true; + } + @Override public String getName() { return keePassFile.meta.databaseName; diff --git a/pom.xml b/pom.xml index 209c2fde..ce993483 100644 --- a/pom.xml +++ b/pom.xml @@ -16,342 +16,339 @@ ~ --> - - 4.0.0 + + 4.0.0 - org.linguafranca.pwdb - KeePassJava2.parent - 3.0.0-SNAPSHOT - - database - test - util - kdb - kdbx-io - kdbx-database - all - example - - pom + org.linguafranca.pwdb + KeePassJava2.parent + 3.0.0-SNAPSHOT + + database + test + util + kdb + kdbx-io + kdbx-database + all + example + + pom - KeePassJava2 - A Java 11 API for databases compatible with the renowned KeePass password safe for Windows - https://github.com/jorabin/KeePassJava2 + KeePassJava2 + A Java 11 API for databases compatible with the renowned KeePass password safe for Windows + + https://github.com/jorabin/KeePassJava2 - - - Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - repo - - + + + Apache License, Version 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + repo + + - - scm:git:git://github.com/jorabin/KeePassJava2.git - scm:git:git@github.com:jorabin/KeePassJava2.git - https://github.com/jorabin/KeePassJava2 - KeePassJava2-3.0.0 - + + scm:git:git://github.com/jorabin/KeePassJava2.git + scm:git:git@github.com:jorabin/KeePassJava2.git + https://github.com/jorabin/KeePassJava2 + KeePassJava2-3.0.0 + - - - Jo Rabin - Europe/London - https://www.linkedin.com/in/jorabin - - Creator - - - + + + Jo Rabin + Europe/London + https://www.linkedin.com/in/jorabin + + Creator + + + - - UTF-8 - 11 + + UTF-8 + 11 - 3.13.0 - 3.5.2 - 1.7.0 - 3.3.1 - 3.11.2 - 3.2.7 - 3.1.1 - 3.4.2 - 3.8.1 - 3.4.0 - 2.18.0 - 3.5.0 + 3.13.0 + 3.5.2 + 1.7.0 + 3.3.1 + 3.11.2 + 3.2.7 + 3.1.1 + 3.4.2 + 3.8.1 + 3.4.0 + 2.18.0 + 3.5.0 - 5.8.1 - 1.8.2 - - false - + + false + - - - - - org.apache.maven.plugins - maven-surefire-plugin - ${maven-surefire-plugin.version} - - false - - - ${inhibitConsoleOutput} - - - - ${maven.multiModuleProjectDirectory}/logback.xml - - - - - listener - org.linguafranca.util.MavenRunListener - - - - - - org.apache.maven.plugins - maven-jar-plugin - ${maven-jar-plugin.version} - - - - ${project.groupId}.${project.artifactId} - - - - - - org.codehaus.mojo - versions-maven-plugin - ${versions-maven-plugin.version} - - - + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + false + + + ${inhibitConsoleOutput} + + + + ${maven.multiModuleProjectDirectory}/logback.xml + + + + + + listener + org.linguafranca.util.MavenRunListener + + + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + ${project.groupId}.${project.artifactId} + + + + + + org.codehaus.mojo + versions-maven-plugin + ${versions-maven-plugin.version} + + + - - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - - - - org.apache.maven.plugins - maven-release-plugin - ${maven-release-plugin.version} - - true - false - releaseToMaven - deploy - - - - org.apache.maven.plugins - maven-dependency-plugin - ${maven-dependency-plugin.version} - - - maven-clean-plugin - ${maven-clean-plugin.version} - - - - testOutput - false - - - - - - org.apache.maven.plugins - maven-enforcer-plugin - ${maven-enforcer-plugin.version} - - - enforce-maven - - enforce - - - - - - 3.6.3 - - - - - - - - + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-release-plugin + ${maven-release-plugin.version} + + true + false + releaseToMaven + deploy + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + + + maven-clean-plugin + ${maven-clean-plugin.version} + + + + testOutput + false + + + + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven-enforcer-plugin.version} + + + enforce-maven + + enforce + + + + + + 3.6.3 + + + + + + + + - - - org.jetbrains - annotations - provided - - - org.junit.jupiter - junit-jupiter-engine - ${junit.version} - - - org.junit.vintage - junit-vintage-engine - ${junit.version} - - - org.junit.platform - junit-platform-launcher - ${junit.platform.version} - - - org.junit.platform - junit-platform-runner - ${junit.platform.version} - - - org.slf4j - slf4j-api - test - - - ch.qos.logback - logback-classic - test - - + + + org.jetbrains + annotations + provided + + + org.junit.platform + junit-platform-launcher + test + + + org.junit.jupiter + junit-jupiter-engine + test + + + org.junit.vintage + junit-vintage-engine + test + + + org.slf4j + slf4j-api + test + + + ch.qos.logback + logback-classic + test + + - - - - org.jetbrains - annotations - 26.0.1 - - - - com.google.guava - guava - 33.4.0-jre - - - org.bouncycastle - bcpkix-jdk18on - 1.79 - - - org.slf4j - slf4j-api - 2.0.16 - - - - ch.qos.logback - logback-classic - 1.5.15 - - - commons-codec - commons-codec - 1.17.1 - - - junit - junit - 4.13.2 - test - - - + + + + org.jetbrains + annotations + 26.0.1 + + + + com.google.guava + guava + 33.4.0-jre + + + org.bouncycastle + bcpkix-jdk18on + 1.79 + + + org.slf4j + slf4j-api + 2.0.16 + + + + ch.qos.logback + logback-classic + 1.5.15 + + + commons-codec + commons-codec + 1.17.1 + + + org.junit + junit-bom + 5.12.0 + pom + import + + + - - - inhibitConsoleOutput - - true - - - - releaseToMaven - - - - org.apache.maven.plugins - maven-gpg-plugin - ${maven-gpg-plugin.version} - - - sign-artifacts - verify - - sign - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - ${nexus-staging-maven-plugin.version} - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-source-plugin - ${maven-source-plugin.version} - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - ${maven-javadoc-plugin.version} - - - attach-javadocs - - jar - - - html, syntax - - - - - - - - + + + inhibitConsoleOutput + + true + + + + releaseToMaven + + + + org.apache.maven.plugins + maven-gpg-plugin + ${maven-gpg-plugin.version} + + + sign-artifacts + verify + + sign + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus-staging-maven-plugin.version} + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + + attach-javadocs + + jar + + + html, syntax + + + + + + + + - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + diff --git a/util/pom.xml b/util/pom.xml index 1aec9411..4e44359a 100644 --- a/util/pom.xml +++ b/util/pom.xml @@ -37,13 +37,14 @@ - junit - junit + org.slf4j + slf4j-api provided - org.slf4j - slf4j-api + org.junit.vintage + junit-vintage-engine + 5.12.0 provided From 9797c3ceb1df9e84e65e2a6c8fae6f5d0ea80d3d Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 11 Mar 2025 12:00:44 +0000 Subject: [PATCH 39/66] tweaks all round --- .../org/linguafranca/pwdb/PropertyValue.java | 30 +++++++++++++---- .../pwdb/basic/BasicDatabase.java | 5 +-- .../org/linguafranca/pwdb/package-info.java | 25 +++++++++++++++ .../pwdb/basic/BasicDatabaseTest.java | 4 +-- ...aseTest.java => GroupsAndEntriesTest.java} | 2 +- .../pwdb/test/ProtectedPropertyTest.java | 11 ++----- .../pwdb/test/TrivialDatabaseTest.java | 4 --- .../linguafranca/pwdb/example/Jackson.java | 32 +++++++++++++++++++ .../SecureCharBufferPropertyValue.java | 3 +- .../linguafranca/pwdb/kdbx/OpenDbExample.java | 2 ++ .../linguafranca/pwdb/kdbx/QuickStart.java | 16 +++++----- 11 files changed, 100 insertions(+), 34 deletions(-) create mode 100644 database/src/main/java/org/linguafranca/pwdb/package-info.java rename database/src/test/java/org/linguafranca/pwdb/test/{CrudDatabaseTest.java => GroupsAndEntriesTest.java} (99%) create mode 100644 example/src/main/java/org/linguafranca/pwdb/example/Jackson.java diff --git a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java index 66dc3d87..43a271b9 100644 --- a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java +++ b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb; -import com.google.common.base.Charsets; - import javax.crypto.Cipher; import javax.crypto.SealedObject; import javax.crypto.SecretKey; @@ -63,7 +61,7 @@ static byte[] charsToBytes(char[] value) { static char[] bytesToChars(byte[] value) { ByteBuffer bb = ByteBuffer.wrap(value); - CharBuffer cb = Charsets.UTF_8.decode(bb); + CharBuffer cb = StandardCharsets.UTF_8.decode(bb); char[] chars = new char[cb.limit()]; cb.get(chars); return chars; @@ -121,6 +119,10 @@ default PropertyValue.Factory getFactoryFor(String prop newUnprotected(); } + /** + * The default implementation of the Strategy interface in which the only protected property is Password. + * It uses SealedStore and BytesStore for protected and unprotected properties. It is immutable. + */ class Default implements Strategy { @Override @@ -138,10 +140,23 @@ public Factory newUnprotected() { return BytesStore.getFactory(); } } + + /** + * A mutable implementation of the Strategy interface, which allows the protected properties to be set. + * By default, the only protected property is Password. + * It uses SealedStore and BytesStore for protected and unprotected properties. + */ + class MutableProtectionStrategy extends PropertyValue.Strategy.Default { + List protectedProperties = new ArrayList<>(); + @Override + public List getProtectedProperties() { + return protectedProperties; + } + } } /** - * Values are stored as strings. + * Property Values are stored as strings. */ class StringStore implements PropertyValue { private final String value; @@ -232,6 +247,7 @@ public BytesStore of(byte[] value) { public static PropertyValue.Factory getFactory() { return factory; } + public BytesStore(CharSequence aString) { this.value = charSequenceToBytes(aString); } @@ -246,13 +262,13 @@ public BytesStore(byte [] value) { @Override public String getValueAsString() { - return new String(this.value); + return new String(this.value, StandardCharsets.UTF_8); } @Override public CharSequence getValue() { ByteBuffer bb = ByteBuffer.wrap(this.value); - return Charsets.UTF_8.decode(bb); + return StandardCharsets.UTF_8.decode(bb); } @Override @@ -431,7 +447,7 @@ public SealedStore(CharSequence charSequence) { @Override public String getValueAsString() { - return new String(getBytes()); + return new String(getBytes(), StandardCharsets.UTF_8); } @Override diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 66f92ae7..ac1f774e 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -23,8 +23,10 @@ import java.io.IOException; import java.io.OutputStream; -import java.util.UUID; +/** + * A basic implementation of a Database + */ public class BasicDatabase extends ProtectedDatabase { private final Group root; private String databaseName; @@ -93,7 +95,6 @@ public String getDescription() { @Override public void setDescription(String description) { this.databaseDescription = description; - } @Override diff --git a/database/src/main/java/org/linguafranca/pwdb/package-info.java b/database/src/main/java/org/linguafranca/pwdb/package-info.java new file mode 100644 index 00000000..a493b42c --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/package-info.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * This package contains the interfaces that define the org.linguafranca Database API. + *

+ * The API is designed to be a simple, low-level, read-write API for password databases. + *

+ * Class instances of the interfaces are contained in the sub-packages of this package. + */ +package org.linguafranca.pwdb; \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java index e51d7d87..73376e7b 100644 --- a/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java @@ -18,14 +18,14 @@ package org.linguafranca.pwdb.basic; import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.test.CrudDatabaseTest; +import org.linguafranca.pwdb.test.GroupsAndEntriesTest; import org.linguafranca.pwdb.test.RecycleBinTest; import org.linguafranca.pwdb.test.ProtectedPropertyTest; import org.linguafranca.pwdb.test.TrivialDatabaseTest; public class BasicDatabaseTest implements TrivialDatabaseTest, - CrudDatabaseTest, + GroupsAndEntriesTest, RecycleBinTest, ProtectedPropertyTest { diff --git a/database/src/test/java/org/linguafranca/pwdb/test/CrudDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java similarity index 99% rename from database/src/test/java/org/linguafranca/pwdb/test/CrudDatabaseTest.java rename to database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java index 3b764b0e..d8ee136e 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/CrudDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java @@ -48,7 +48,7 @@ /** * @author Jo */ -public interface CrudDatabaseTest { +public interface GroupsAndEntriesTest { void newDatabase(); Database getDatabase(); Database createDatabase(); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java index 617d8797..894c926a 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java @@ -31,13 +31,6 @@ public interface ProtectedPropertyTest { - class MutableProtectionStrategy extends PropertyValue.Strategy.Default { - List protectedProperties = new ArrayList<>(); - @Override - public List getProtectedProperties() { - return protectedProperties; - } - } void newDatabase(); Database getDatabase(); @@ -58,7 +51,7 @@ default void testShouldProtect() { @Test default void testSetShouldProtect() { - getDatabase().setPropertyValueStrategy(new MutableProtectionStrategy()); + getDatabase().setPropertyValueStrategy(new PropertyValue.Strategy.MutableProtectionStrategy()); String propertyName = PASSWORD; getDatabase().setShouldProtect(propertyName, true); assertTrue(getDatabase().shouldProtect(propertyName)); @@ -69,7 +62,7 @@ default void testSetShouldProtect() { @Test default void testListShouldProtect() { - getDatabase().setPropertyValueStrategy(new MutableProtectionStrategy()); + getDatabase().setPropertyValueStrategy(new PropertyValue.Strategy.MutableProtectionStrategy()); String propertyName1 = PASSWORD; String propertyName2 = USER_NAME; getDatabase().setShouldProtect(propertyName1, true); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java index 4b8962e7..b06a4145 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java @@ -25,13 +25,9 @@ import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; -import static org.junit.jupiter.api.Assumptions.assumeTrue; @TestInstance(TestInstance.Lifecycle.PER_CLASS) public interface TrivialDatabaseTest { diff --git a/example/src/main/java/org/linguafranca/pwdb/example/Jackson.java b/example/src/main/java/org/linguafranca/pwdb/example/Jackson.java new file mode 100644 index 00000000..86f07bf4 --- /dev/null +++ b/example/src/main/java/org/linguafranca/pwdb/example/Jackson.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.example; + +public class Jackson { + public static class Reader { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } + + public static class Writer { + public static void main(String[] args) { + System.out.println("Hello, World!"); + } + } +} diff --git a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java index 652385bd..8b2708f0 100644 --- a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java +++ b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java @@ -25,7 +25,8 @@ import java.nio.charset.StandardCharsets; /** - * SecureCharBufferPropertyValue uses {@link SecureCharBuffer} + * This is an example of a PropertyValue implementation that uses {@link SecureCharBuffer} + * to store the value in memory. */ public class SecureCharBufferPropertyValue implements PropertyValue { private final SecureCharBuffer value; diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index de2b96d0..f32ed3d4 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -29,6 +29,8 @@ /** * Example and naive speed test for various implementations. + *

+ * This isn't really very interesting in V3 but remains here in case new implementations are added. * * @author jo */ diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index f1a64c62..b01c8a79 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -54,7 +54,7 @@ public void canonicalQuickStart() throws IOException { // value strategy being to protect field "Password" and no other, all of which are stored as byte[] database.setPropertyValueStrategy(new PropertyValue.Strategy.Default()); // V3 Group is not generified. create a group - // V3 add a new group to another group with the name supplied + // V3 short-cut add a new group to another group with the name supplied Group main = database.getRootGroup().addGroup("Main"); // V3 fluent building of Entry (using PropertyValueStrategy as set in database to determine protection) main.addEntry() @@ -104,14 +104,14 @@ public void fluentExample() { } /** - * Load KDBX + * Load KDBX from file */ public void loadKdbx() throws IOException { // get an input stream try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx")) { // password credentials Credentials credentials = new KdbxCredentials("123".getBytes()); - // Jaxb implementation seems a lot faster than the DOM implementation + // load the database from the input stream Database database = KdbxDatabase.load(credentials, inputStream); // visit all groups and entries and list them to console database.visit(new Visitor.Print(printStream)); @@ -119,7 +119,7 @@ public void loadKdbx() throws IOException { } /** - * Save KDBX + * Save KDBX to file */ private Entry entryFactory(Database database, String s, int e) { @@ -180,7 +180,6 @@ public void loadKdbSaveKdbx() throws IOException { Credentials credentials = new KdbCredentials.Password("123".getBytes()); // load KdbDatabase KdbDatabase database = KdbDatabase.load(credentials, inputStream); - // visit all groups and entries and list them to console // create a KDBX (database Database kdbxDatabase = new KdbxDatabase(); @@ -210,17 +209,18 @@ public void loadKdbx3SaveKdbx4(String v3ResourceName, byte[] password, Path v4Fi Database kdbxDatabase = new KdbxDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDBX 3 Database to KDBX 4 Database"); - // deep copy from group (not including source group, KDB database has simulated root) + // deep copy from group (not including source group) kdbxDatabase.getRootGroup().copy(database.getRootGroup()); // choose a stream format - V4 Kdbx and choose some algorithms KdbxHeader kdbxHeader = new KdbxHeader(KdbxHeader.KdbxHeaderOpts.V4_AES_ARGON_CHA_CHA); - KdbxStreamFormat formatV4 = new KdbxStreamFormat(kdbxHeader); // change algorithm from those originally selected kdbxHeader.setCipherAlgorithm(Encryption.Cipher.CHA_CHA_20); kdbxHeader.setKeyDerivationFunction(Encryption.KeyDerivationFunction.ARGON2); kdbxHeader.setProtectedStreamAlgorithm(Encryption.ProtectedStreamAlgorithm.CHA_CHA_20); + KdbxStreamFormat formatV4 = new KdbxStreamFormat(kdbxHeader); + // save it with format options try (OutputStream v4OutputStream = Files.newOutputStream(v4Filename)) { kdbxDatabase.save(formatV4, credentials, v4OutputStream); @@ -240,7 +240,7 @@ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3File database = KdbxDatabase.load(credentials, inputStream); } - // create a KDBX (database) + // create a KDBX database Database kdbxDatabase = new KdbxDatabase(); kdbxDatabase.setName("New Database"); kdbxDatabase.setDescription("Migration of KDBX 4 Database to KDBX 3 Database"); From 273d64855c1894f14108a8bfdfb89917d71bfeb8 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 11 Mar 2025 22:05:05 +0000 Subject: [PATCH 40/66] separate out BasicDatabase and make serializable --- dblite/pom.xml | 67 ++++++ .../pwdb/basic/BasicDatabase.java | 47 ++++- .../pwdb/basic/BasicDatabaseSerializer.java | 193 ++++++++++++++++++ .../linguafranca/pwdb/basic/BasicEntry.java | 41 +++- .../linguafranca/pwdb/basic/BasicGroup.java | 16 +- .../linguafranca/pwdb/basic/BasicIcon.java | 0 .../basic/BasicDatabaseSerializerTest.java | 96 +++++++++ .../pwdb/basic/BasicDatabaseTest.java | 0 .../pwdb/basic/BasicEntryTest.java | 0 .../pwdb/basic/BasicGroupTest.java | 0 .../linguafranca/pwdb/example/Jackson.java | 32 --- pom.xml | 1 + 12 files changed, 444 insertions(+), 49 deletions(-) create mode 100644 dblite/pom.xml rename {database => dblite}/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java (69%) create mode 100644 dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java rename {database => dblite}/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java (82%) rename {database => dblite}/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java (94%) rename {database => dblite}/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java (100%) create mode 100644 dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java rename {database => dblite}/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java (100%) rename {database => dblite}/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java (100%) rename {database => dblite}/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java (100%) delete mode 100644 example/src/main/java/org/linguafranca/pwdb/example/Jackson.java diff --git a/dblite/pom.xml b/dblite/pom.xml new file mode 100644 index 00000000..4c2f1f47 --- /dev/null +++ b/dblite/pom.xml @@ -0,0 +1,67 @@ + + + + + 4.0.0 + + org.linguafranca.pwdb + KeePassJava2.parent + 3.0.0-SNAPSHOT + + + dblite + + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.18.2 + + + com.fasterxml.woodstox + woodstox-core + 7.1.0 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.18.2 + + + org.linguafranca.pwdb + database + 3.0.0-SNAPSHOT + compile + + + org.linguafranca.pwdb + test + ${project.version} + test-jar + + + org.linguafranca.pwdb + database + ${project.version} + test-jar + + + + \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java similarity index 69% rename from database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java rename to dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index ac1f774e..4502fe33 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -23,20 +23,16 @@ import java.io.IOException; import java.io.OutputStream; +import java.util.Stack; /** * A basic implementation of a Database */ public class BasicDatabase extends ProtectedDatabase { - private final Group root; + private final BasicGroup root = new BasicGroup(this, "Root"); private String databaseName; private String databaseDescription; - public BasicDatabase() { - root = new BasicGroup(this); - root.setName("Root"); - } - @Override public Group getRootGroup() { return root; @@ -111,4 +107,43 @@ public void save(StreamFormat streamFormat, C public StreamFormat getStreamFormat() { throw new UnsupportedOperationException(); } + + /** + * A visitor that can be used to fix up the parent and database fields of groups and entries + * after deserialization of the database + */ + public static class FixupVisitor implements Visitor{ + final Stack stack = new Stack<>(); + BasicDatabase database; + + public FixupVisitor(BasicDatabase database) { + this.database = database; + } + + @Override + public void startVisit(Group group) { + ((BasicGroup) group).database = database; + if (!stack.isEmpty()) { + ((BasicGroup) group).parent = (BasicGroup) stack.peek(); + } + stack.push(group); + } + + @Override + public void endVisit(Group group) { + stack.pop(); + } + + @Override + public void visit(Entry entry) { + ((BasicEntry) entry).database = database; + ((BasicEntry) entry).parent = stack.peek(); + } + + @Override + public boolean isEntriesFirst() { + return false; + } + + }; } diff --git a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java new file mode 100644 index 00000000..a79c2163 --- /dev/null +++ b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import com.fasterxml.jackson.annotation.JsonAutoDetect; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.module.SimpleModule; +import com.fasterxml.jackson.dataformat.xml.XmlMapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; +import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; +import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.linguafranca.pwdb.PropertyValue; +import org.linguafranca.pwdb.protect.ProtectedDatabase; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.List; + +/** + * This class is responsible for serializing and deserializing a BasicDatabase to and from a stream. + */ +public interface BasicDatabaseSerializer { + + /** + * Serialize a BasicDatabase to a stream + * + * @param database the database to serialize + * @param outputStream the stream to serialize to + */ + void save(BasicDatabase database, OutputStream outputStream) throws IOException; + + /** + * Deserialize a BasicDatabase from a stream + * + * @param inputStream the serialized form of the database + * @return the deserialized database + */ + BasicDatabase load(InputStream inputStream) throws IOException; + + /** + * XML implementation of containing BasicDatabaseSerializer + */ + class Xml implements BasicDatabaseSerializer { + private final ObjectMapper objectMapper = init(); + + @Override + public void save(BasicDatabase database, OutputStream outputStream) throws IOException { + objectMapper.writer().withRootName("database").writeValue(outputStream, database); + } + + @Override + public BasicDatabase load(InputStream inputStream) throws IOException { + BasicDatabase database2 = objectMapper.readValue(inputStream, BasicDatabase.class); + database2.visit(new BasicDatabase.FixupVisitor(database2)); + return database2; + } + + private ObjectMapper init() { + SimpleModule module = new SimpleModule() + .addDeserializer(PropertyValue.class, new PropertyValueDeserializer()) + .addSerializer(PropertyValue.class, new PropertyValueSerializer()); + XmlMapper mapper = XmlMapper.builder() + // ignore everything except fields + .visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) + .visibility(PropertyAccessor.GETTER, JsonAutoDetect.Visibility.NONE) + .visibility(PropertyAccessor.IS_GETTER, JsonAutoDetect.Visibility.NONE) + .visibility(PropertyAccessor.SETTER, JsonAutoDetect.Visibility.NONE) + .visibility(PropertyAccessor.CREATOR, JsonAutoDetect.Visibility.NONE) + .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS) + // even though the source file is not annotated, we want annotations in the Configuration.class + .enable(MapperFeature.USE_ANNOTATIONS) + // add mixin to main classes + .addMixIn(BasicGroup.class, Configuration.class) + .addMixIn(BasicEntry.class, Configuration.class) + .addMixIn(BasicDatabase.class, Configuration.class) + .build(); + + + mapper + // for PropertyValue serialization + .registerModule(module) + // for Instant processing + .registerModule(new JavaTimeModule()) + // pretty print + .enable(SerializationFeature.INDENT_OUTPUT) + // suppress empty fields + .setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + + return mapper; + } + + /** + * Configure special treatment on serialization of properties + */ + public static class PropertyValueSerializer extends JsonSerializer { + /** + * Don't serialize empty properties + *

+ * TODO: at present, policy is to have all 5 standard properties present with empty + * value if necessary. Review policy. Deserialization messes up constructor + * behaviour which is to initialise those properties that way. + */ + @Override + public boolean isEmpty(SerializerProvider provider, PropertyValue value) { + // TODO: return (value.getValueAsString().isEmpty()); + return false; + } + + /** + * write protected properties with a flag to show they need to be protected on input + */ + @Override + public void serialize(PropertyValue value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + + final ToXmlGenerator xmlGenerator = (ToXmlGenerator) gen; + xmlGenerator.writeStartObject(); + + if (value.isProtected()) { + xmlGenerator.setNextIsAttribute(true); + xmlGenerator.writeStringField("protected", "true"); + } + xmlGenerator.setNextIsAttribute(false); + xmlGenerator.setNextIsUnwrapped(true); + xmlGenerator.writeStringField("text", value.getValueAsString()); + xmlGenerator.writeEndObject(); + } + } + + /** + * Property deserialization + */ + public static class PropertyValueDeserializer extends JsonDeserializer { + PropertyValue.Strategy strategy = new PropertyValue.Strategy.Default(); + + @Override + public PropertyValue deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if (node.isTextual()) { + return strategy.newUnprotected().of(node.textValue()); + } + if (node.has("protected")) { + return strategy.newProtected().of(node.get("").asText()); + } + throw new IllegalStateException("Unknown property value format " + node); + } + } + + /** + * Class is for binding Jackson annotations to a later Jackson "Mix-In" + */ + public abstract static class Configuration { + // Tell Jackson how to treat lists of Entry and Groups + @JacksonXmlProperty(localName = "entry") + @JacksonXmlElementWrapper(useWrapping = false) + protected List entries; + @JacksonXmlProperty(localName = "group") + @JacksonXmlElementWrapper(useWrapping = false) + protected List groups; + // tell Jackson to ignore these fields + @JsonIgnore + BasicGroup parent; + @JsonIgnore + BasicDatabase database; + @JsonIgnore + ProtectedDatabase valueStrategy; + @JsonIgnore + boolean isDirty; + } + } +} diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java similarity index 82% rename from database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java rename to dblite/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java index e8d812d2..bd5e3029 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java +++ b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java @@ -29,21 +29,20 @@ import java.util.*; public class BasicEntry extends AbstractEntry { - private final BasicDatabase database; + BasicDatabase database; private final UUID uuid; private final Map properties = new HashMap<>(); private final Map binaries = new HashMap<>(); private final Instant creationTime; protected Group parent; - private Icon icon; + private BasicIcon icon; private Instant expiryTime; private Instant accessTime; private Instant modifiedTime; private boolean isExpires; - public BasicEntry(BasicDatabase basicDatabase) { - this.database = basicDatabase; + public BasicEntry() { this.uuid = UUID.randomUUID(); this.creationTime = Instant.now(); for (String name : Entry.STANDARD_PROPERTY_NAMES) { @@ -51,6 +50,11 @@ public BasicEntry(BasicDatabase basicDatabase) { } } + public BasicEntry(BasicDatabase basicDatabase) { + this(); + this.database = basicDatabase; + } + @Override protected void touch() { database.setDirty(true); @@ -58,6 +62,9 @@ protected void touch() { @Override public String getProperty(String name) { + if (!STANDARD_PROPERTY_NAMES.contains(name) && !database.supportsNonStandardPropertyNames()) { + throw new IllegalArgumentException("Property " + name + " is not a standard property name"); + } if (!properties.containsKey(name)) { return null; } @@ -93,6 +100,30 @@ public List getPropertyNames() { return new ArrayList<>(properties.keySet()); } + @Override + public PropertyValue getPropertyValue(String name) { + return properties.get(name); + } + + @Override + public Entry setPropertyValue(String name, PropertyValue value) { + properties.put(name, value); + return this; + } + + public Entry addProperty(String name, byte[] value){ + properties.put(name, database.getPropertyValueStrategy().getFactoryFor(name).of(value)); + return this; + } + public Entry addProperty(String name, char[] value) { + properties.put(name, database.getPropertyValueStrategy().getFactoryFor(name).of(value)); + return this; + } + + public Entry addProperty(String name, CharSequence value){ + properties.put(name, database.getPropertyValueStrategy().getFactoryFor(name).of(value)); + return this; + } @Override public byte[] getBinaryProperty(String name) { if (!binaries.containsKey(name)) { @@ -138,7 +169,7 @@ public Icon getIcon() { @Override public void setIcon(Icon icon) { - this.icon = icon; + this.icon = (BasicIcon) icon; } @Override diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java similarity index 94% rename from database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java rename to dblite/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java index 6259a8c4..6b5cb012 100644 --- a/database/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java +++ b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java @@ -31,20 +31,24 @@ import java.util.UUID; public class BasicGroup extends AbstractGroup { - private BasicGroup parent; + BasicGroup parent; private final List groups = new ArrayList<>(); private final List entries = new ArrayList<>(); private String name; - private Icon icon; + private BasicIcon icon; private final UUID uuid; - private final @NotNull BasicDatabase database; + BasicDatabase database; - BasicGroup(@NotNull BasicDatabase database) { - this.database = database; + BasicGroup () { + this.database = null; this.uuid = UUID.randomUUID(); this.icon = new BasicIcon(); this.name = ""; } + BasicGroup(@NotNull BasicDatabase database) { + this(); + this.database = database; + } BasicGroup(@NotNull BasicDatabase database, String name) { this(database); @@ -145,7 +149,7 @@ public Icon getIcon() { @Override public void setIcon(Icon icon) { - this.icon = icon; + this.icon = (BasicIcon) icon; } @Override diff --git a/database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java b/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java similarity index 100% rename from database/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java rename to dblite/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java diff --git a/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java b/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java new file mode 100644 index 00000000..c6e5e70e --- /dev/null +++ b/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Group; +import org.linguafranca.pwdb.Visitor; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class BasicDatabaseSerializerTest { + public static final String TEST_OUTPUT_DIR = "testOutput/"; + static BasicDatabase database; + + @BeforeAll + static void setUp() throws IOException { + Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); + database = new BasicDatabase(); + database.setName("My Database"); + database.setDescription("A database of passwords"); + database.getRootGroup().addGroup("Group 1") + .addEntry() + .addProperty(Entry.STANDARD_PROPERTY_NAME.TITLE, "Entry 1") + .addProperty(Entry.STANDARD_PROPERTY_NAME.USER_NAME, "John Doe") + .addProperty(Entry.STANDARD_PROPERTY_NAME.PASSWORD, "password123".getBytes()) + .addEntry() + .addProperty(Entry.STANDARD_PROPERTY_NAME.TITLE, "Entry 2") + .addProperty(Entry.STANDARD_PROPERTY_NAME.USER_NAME, "Jane Doe") + .addProperty(Entry.STANDARD_PROPERTY_NAME.PASSWORD, "password123".getBytes()) + .addProperty("non-standard", "non-standard value"); + } + + Visitor visitor = new Visitor() { + @Override + public void startVisit(Group group) { + System.out.println("Group: " + group.getName() + " " + group.getUuid()); + //assertNotNull(database.findGroup(group.getUuid())); + } + + @Override + public void endVisit(Group group) { + + } + + @Override + public void visit(Entry entry) { + System.out.println("Entry: " + entry.getTitle() + " " + entry.getUuid()); + for (String propertyName: entry.getPropertyNames()) { + System.out.println(" " + propertyName + ": " + entry.getPropertyValue(propertyName)); + } + Entry entry1 = database.findEntry(entry.getUuid()); + assertNotNull(entry1, "Entry not found " + entry.getUuid()); + assertEquals(entry.getPropertyNames().size(), entry1.getPropertyNames().size()); + } + + @Override + public boolean isEntriesFirst() { + return false; + } + }; + + @Test + void saveAndLoad() throws IOException { + new BasicDatabaseSerializer.Xml().save(database, new FileOutputStream(TEST_OUTPUT_DIR + "test.xml")); + BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml().load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); + loadedDatabase.visit(visitor); + } + + @Test + void load() throws IOException { + BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml().load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); + loadedDatabase.visit(new Visitor.Print()); + } +} \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java similarity index 100% rename from database/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java rename to dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java b/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java similarity index 100% rename from database/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java rename to dblite/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java diff --git a/database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java b/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java similarity index 100% rename from database/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java rename to dblite/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java diff --git a/example/src/main/java/org/linguafranca/pwdb/example/Jackson.java b/example/src/main/java/org/linguafranca/pwdb/example/Jackson.java deleted file mode 100644 index 86f07bf4..00000000 --- a/example/src/main/java/org/linguafranca/pwdb/example/Jackson.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.example; - -public class Jackson { - public static class Reader { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } - } - - public static class Writer { - public static void main(String[] args) { - System.out.println("Hello, World!"); - } - } -} diff --git a/pom.xml b/pom.xml index ce993483..6934a408 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,7 @@ kdbx-database all example + dblite pom From 96de3a9d1695d5608a5f3b1518d1b972db53f13b Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Wed, 12 Mar 2025 14:28:53 +0000 Subject: [PATCH 41/66] more refactoring and sorting out serialization shenanigans --- {dblite => basic}/pom.xml | 19 ++--- .../pwdb/basic/BasicDatabase.java | 0 .../pwdb/basic/BasicDatabaseSerializer.java | 72 ++++++++++++++++--- .../linguafranca/pwdb/basic/BasicEntry.java | 9 ++- .../linguafranca/pwdb/basic/BasicGroup.java | 11 +-- .../linguafranca/pwdb/basic/BasicIcon.java | 0 .../basic/BasicDatabaseSerializerTest.java | 24 ++++--- .../pwdb/basic/BasicDatabaseTest.java | 0 .../pwdb/basic/BasicEntryTest.java | 0 .../pwdb/basic/BasicGroupTest.java | 0 .../java/org/linguafranca/pwdb/Entry.java | 9 +-- .../org/linguafranca/pwdb/PropertyValue.java | 3 + kdbx-database/pom.xml | 2 +- kdbx-io/pom.xml | 2 +- pom.xml | 2 +- 15 files changed, 112 insertions(+), 41 deletions(-) rename {dblite => basic}/pom.xml (95%) rename {dblite => basic}/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java (100%) rename {dblite => basic}/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java (73%) rename {dblite => basic}/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java (99%) rename {dblite => basic}/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java (99%) rename {dblite => basic}/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java (100%) rename {dblite => basic}/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java (76%) rename {dblite => basic}/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java (100%) rename {dblite => basic}/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java (100%) rename {dblite => basic}/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java (100%) diff --git a/dblite/pom.xml b/basic/pom.xml similarity index 95% rename from dblite/pom.xml rename to basic/pom.xml index 4c2f1f47..d9f27278 100644 --- a/dblite/pom.xml +++ b/basic/pom.xml @@ -26,7 +26,8 @@ 3.0.0-SNAPSHOT - dblite + basic + PWDB :: Basic @@ -34,31 +35,31 @@ jackson-dataformat-xml 2.18.2 - - com.fasterxml.woodstox - woodstox-core - 7.1.0 - com.fasterxml.jackson.datatype jackson-datatype-jsr310 2.18.2 + + com.fasterxml.woodstox + woodstox-core + 7.1.0 + org.linguafranca.pwdb database - 3.0.0-SNAPSHOT + ${project.version} compile org.linguafranca.pwdb - test + database ${project.version} test-jar org.linguafranca.pwdb - database + test ${project.version} test-jar diff --git a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java similarity index 100% rename from dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java rename to basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java diff --git a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java similarity index 73% rename from dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java rename to basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java index a79c2163..3f707761 100644 --- a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java @@ -17,10 +17,7 @@ package org.linguafranca.pwdb.basic; -import com.fasterxml.jackson.annotation.JsonAutoDetect; -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.PropertyAccessor; +import com.fasterxml.jackson.annotation.*; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.*; @@ -80,7 +77,9 @@ public BasicDatabase load(InputStream inputStream) throws IOException { private ObjectMapper init() { SimpleModule module = new SimpleModule() .addDeserializer(PropertyValue.class, new PropertyValueDeserializer()) - .addSerializer(PropertyValue.class, new PropertyValueSerializer()); + .addSerializer(PropertyValue.class, new PropertyValueSerializer()) + .addDeserializer(BasicIcon.class, new IconDeserializer()) + .addSerializer(BasicIcon.class, new IconSerializer()); XmlMapper mapper = XmlMapper.builder() // ignore everything except fields .visibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY) @@ -135,15 +134,29 @@ public boolean isEmpty(SerializerProvider provider, PropertyValue value) { public void serialize(PropertyValue value, JsonGenerator gen, SerializerProvider serializers) throws IOException { final ToXmlGenerator xmlGenerator = (ToXmlGenerator) gen; + // find out of we are serializing binaries + Object c = xmlGenerator.getOutputContext().getParent().getCurrentName(); + boolean asBinary = c.equals("binaries"); xmlGenerator.writeStartObject(); if (value.isProtected()) { xmlGenerator.setNextIsAttribute(true); xmlGenerator.writeStringField("protected", "true"); + asBinary = true; // write password as binary TODO later we can stream encode it } + + if (asBinary) { + xmlGenerator.setNextIsAttribute(true); + xmlGenerator.writeStringField("binary", "true"); + } + xmlGenerator.setNextIsAttribute(false); xmlGenerator.setNextIsUnwrapped(true); - xmlGenerator.writeStringField("text", value.getValueAsString()); + if (asBinary) { + xmlGenerator.writeBinaryField("text", value.getValueAsBytes()); + } else { + xmlGenerator.writeStringField("text", value.getValueAsString()); + } xmlGenerator.writeEndObject(); } } @@ -161,21 +174,64 @@ public PropertyValue deserialize(JsonParser jsonParser, DeserializationContext d if (node.isTextual()) { return strategy.newUnprotected().of(node.textValue()); } - if (node.has("protected")) { - return strategy.newProtected().of(node.get("").asText()); + if (node.has("protected") || node.has("binary")) { + return strategy.newProtected().of(node.get("").binaryValue()); } throw new IllegalStateException("Unknown property value format " + node); } } + /** + * Configure special treatment for serialization of icons + */ + public static class IconSerializer extends JsonSerializer { + /** + * flatten icon representation + */ + @Override + public void serialize(BasicIcon icon, JsonGenerator gen, SerializerProvider serializers) throws IOException { + + final ToXmlGenerator xmlGenerator = (ToXmlGenerator) gen; + xmlGenerator.writeStartObject(); + xmlGenerator.setNextIsAttribute(true); + xmlGenerator.writeStringField("index", String.valueOf(icon.getIndex())); + xmlGenerator.writeEndObject(); + } + } + + /** + * Icon deserialization + */ + public static class IconDeserializer extends JsonDeserializer { + PropertyValue.Strategy strategy = new PropertyValue.Strategy.Default(); + + @Override + public BasicIcon deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if (node.has("index")) { + return new BasicIcon(node.get("index").asInt()); + } + throw new IllegalStateException("Unknown icon value format " + node); + } + } + /** * Class is for binding Jackson annotations to a later Jackson "Mix-In" */ public abstract static class Configuration { // Tell Jackson how to treat lists of Entry and Groups + @JsonPropertyOrder({ "uuid", "icon", "properties", "binaries" }) + protected static class BasicEntry {} + @JacksonXmlProperty(localName = "entry") @JacksonXmlElementWrapper(useWrapping = false) + @JsonPropertyOrder({ "uuid", "icon", "properties", "binaries" }) protected List entries; + + @JsonPropertyOrder({ "uuid", "name", "icon", "group", "entry" }) + protected static class BasicGroup {}; + @JacksonXmlProperty(localName = "group") @JacksonXmlElementWrapper(useWrapping = false) protected List groups; diff --git a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java similarity index 99% rename from dblite/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java rename to basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java index bd5e3029..a0189391 100644 --- a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java @@ -30,21 +30,24 @@ public class BasicEntry extends AbstractEntry { BasicDatabase database; + protected Group parent; + private final UUID uuid; + private BasicIcon icon; private final Map properties = new HashMap<>(); private final Map binaries = new HashMap<>(); + private final Instant creationTime; - protected Group parent; - private BasicIcon icon; - private Instant expiryTime; private Instant accessTime; private Instant modifiedTime; + private Instant expiryTime; private boolean isExpires; public BasicEntry() { this.uuid = UUID.randomUUID(); this.creationTime = Instant.now(); + this.icon = new BasicIcon(1); for (String name : Entry.STANDARD_PROPERTY_NAMES) { properties.put(name, new PropertyValue.StringStore("")); } diff --git a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java similarity index 99% rename from dblite/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java rename to basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java index 6b5cb012..4b173d9e 100644 --- a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java @@ -32,12 +32,15 @@ public class BasicGroup extends AbstractGroup { BasicGroup parent; - private final List groups = new ArrayList<>(); - private final List entries = new ArrayList<>(); + BasicDatabase database; + private final UUID uuid; private String name; private BasicIcon icon; - private final UUID uuid; - BasicDatabase database; + + private final List groups = new ArrayList<>(); + private final List entries = new ArrayList<>(); + + BasicGroup () { this.database = null; diff --git a/dblite/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java similarity index 100% rename from dblite/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java rename to basic/src/main/java/org/linguafranca/pwdb/basic/BasicIcon.java diff --git a/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java similarity index 76% rename from dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java rename to basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java index c6e5e70e..19ee51af 100644 --- a/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java @@ -26,6 +26,7 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -41,15 +42,15 @@ static void setUp() throws IOException { database.setName("My Database"); database.setDescription("A database of passwords"); database.getRootGroup().addGroup("Group 1") - .addEntry() - .addProperty(Entry.STANDARD_PROPERTY_NAME.TITLE, "Entry 1") - .addProperty(Entry.STANDARD_PROPERTY_NAME.USER_NAME, "John Doe") - .addProperty(Entry.STANDARD_PROPERTY_NAME.PASSWORD, "password123".getBytes()) - .addEntry() - .addProperty(Entry.STANDARD_PROPERTY_NAME.TITLE, "Entry 2") - .addProperty(Entry.STANDARD_PROPERTY_NAME.USER_NAME, "Jane Doe") - .addProperty(Entry.STANDARD_PROPERTY_NAME.PASSWORD, "password123".getBytes()) - .addProperty("non-standard", "non-standard value"); + .addEntry("Entry 1") + .addProperty(Entry.STANDARD_PROPERTY_NAME.USER_NAME, "John Doe") + .addProperty(Entry.STANDARD_PROPERTY_NAME.PASSWORD, "password123".getBytes()) + .addEntry("Entry 2") + .addProperty(Entry.STANDARD_PROPERTY_NAME.USER_NAME, "Jane Doe") + .addProperty(Entry.STANDARD_PROPERTY_NAME.PASSWORD, "password123".getBytes()) + .addProperty("non-standard", "non-standard value") + .addEntry("Entry 3") + .setBinaryProperty("gif", new byte[]{1, 2, 3, 4, 5}); } Visitor visitor = new Visitor() { @@ -68,8 +69,11 @@ public void endVisit(Group group) { public void visit(Entry entry) { System.out.println("Entry: " + entry.getTitle() + " " + entry.getUuid()); for (String propertyName: entry.getPropertyNames()) { - System.out.println(" " + propertyName + ": " + entry.getPropertyValue(propertyName)); + System.out.println(" " + propertyName + ": " + entry.getPropertyValue(propertyName).getValueAsString()); } + for (String propertyName: entry.getBinaryPropertyNames()) { + System.out.println(" " + propertyName + ": " + Arrays.toString(entry.getBinaryProperty(propertyName))); + } Entry entry1 = database.findEntry(entry.getUuid()); assertNotNull(entry1, "Entry not found " + entry.getUuid()); assertEquals(entry.getPropertyNames().size(), entry1.getPropertyNames().size()); diff --git a/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java similarity index 100% rename from dblite/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java rename to basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java diff --git a/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java similarity index 100% rename from dblite/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java rename to basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java diff --git a/dblite/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java similarity index 100% rename from dblite/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java rename to basic/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 321b1c5c..c96efc97 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -50,25 +50,26 @@ public interface Entry { * dedicated methods, such as getPassword, or by {@link #getProperty(String)} */ + String STANDARD_PROPERTY_NAME_TITLE = "Title"; String STANDARD_PROPERTY_NAME_USER_NAME = "UserName"; String STANDARD_PROPERTY_NAME_PASSWORD = "Password"; String STANDARD_PROPERTY_NAME_URL = "URL"; - String STANDARD_PROPERTY_NAME_TITLE = "Title"; String STANDARD_PROPERTY_NAME_NOTES = "Notes"; /** allows for static import of name */ interface STANDARD_PROPERTY_NAME { + String TITLE = STANDARD_PROPERTY_NAME_TITLE; String USER_NAME = STANDARD_PROPERTY_NAME_USER_NAME; String PASSWORD = STANDARD_PROPERTY_NAME_PASSWORD; String URL = STANDARD_PROPERTY_NAME_URL; - String TITLE = STANDARD_PROPERTY_NAME_TITLE; String NOTES = STANDARD_PROPERTY_NAME_NOTES; } - List STANDARD_PROPERTY_NAMES = List.of(STANDARD_PROPERTY_NAME_USER_NAME, + List STANDARD_PROPERTY_NAMES = List.of( + STANDARD_PROPERTY_NAME_TITLE, + STANDARD_PROPERTY_NAME_USER_NAME, STANDARD_PROPERTY_NAME_PASSWORD, STANDARD_PROPERTY_NAME_URL, - STANDARD_PROPERTY_NAME_TITLE, STANDARD_PROPERTY_NAME_NOTES); /** diff --git a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java index 43a271b9..b716b94e 100644 --- a/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java +++ b/database/src/main/java/org/linguafranca/pwdb/PropertyValue.java @@ -148,6 +148,9 @@ public Factory newUnprotected() { */ class MutableProtectionStrategy extends PropertyValue.Strategy.Default { List protectedProperties = new ArrayList<>(); + { + protectedProperties.add(Entry.STANDARD_PROPERTY_NAME_PASSWORD); + } @Override public List getProtectedProperties() { return protectedProperties; diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index 43016227..c00a959e 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -25,7 +25,7 @@ 4.0.0 KeePassJava2.kdbx.database - KeePassJava2 :: Jackson + KeePassJava2 :: KDBX-Database Contains a Jackson XML implementation for KDBX. diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index b82da458..ac9d8be6 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -26,7 +26,7 @@ 4.0.0 KeePassJava2.kdbx.io - KeePassJava2 :: KDBX + KeePassJava2 :: KDBX-IO Base classes and resources for other KDBX modules. KDBX Stream Support. Security support. diff --git a/pom.xml b/pom.xml index 6934a408..f68a4b88 100644 --- a/pom.xml +++ b/pom.xml @@ -32,7 +32,7 @@ kdbx-database all example - dblite + basic pom From 447a23478d2bdbe0ad9ed2135cf9cdaac2cac47c Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Wed, 12 Mar 2025 14:42:39 +0000 Subject: [PATCH 42/66] clean compile --- .../pwdb/basic/BasicEntryTest.java | 6 +++--- .../pwdb/protect/ProtectedDatabase.java | 21 ++++++++++++------- .../pwdb/test/ProtectedPropertyTest.java | 2 +- 3 files changed, 17 insertions(+), 12 deletions(-) diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java index 45f46862..a93bf731 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java @@ -110,13 +110,13 @@ void testGetUuid() { @Test void testGetIcon() { - assertNull(entry.getIcon()); + assertNotNull(entry.getIcon()); } @Test void testSetIcon() { - entry.setIcon(new BasicIcon() {}); - assertNotNull(entry.getIcon()); + entry.setIcon(new BasicIcon(1) {}); + assertEquals(1, entry.getIcon().getIndex()); } @Test diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java index 5e98092f..200f6209 100644 --- a/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/protect/ProtectedDatabase.java @@ -17,7 +17,8 @@ package org.linguafranca.pwdb.protect; -import org.linguafranca.pwdb.*; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.abstractdb.AbstractDatabase; import java.util.Collections; @@ -34,21 +35,23 @@ public abstract class ProtectedDatabase extends AbstractDatabase { private PropertyValue.Strategy valueStrategy = new PropertyValue.Strategy.Default(); @Override - public boolean shouldProtect(String propertyName){ + public boolean shouldProtect(String propertyName) { return valueStrategy.getProtectedProperties().contains(propertyName); } @Override - public void setShouldProtect(String propertyName, boolean protect){ + public void setShouldProtect(String propertyName, boolean protect) { if (protect) { - valueStrategy.getProtectedProperties().add(propertyName); + if (!valueStrategy.getProtectedProperties().contains(propertyName)) { + valueStrategy.getProtectedProperties().add(propertyName); + } } else { valueStrategy.getProtectedProperties().remove(propertyName); } } @Override - public List listShouldProtect(){ + public List listShouldProtect() { return Collections.unmodifiableList(valueStrategy.getProtectedProperties()); } @@ -56,20 +59,22 @@ public List listShouldProtect(){ * Get the default means of storage of unprotected and protected property values */ @Override - public PropertyValue.Strategy getPropertyValueStrategy(){ + public PropertyValue.Strategy getPropertyValueStrategy() { return this.valueStrategy; } + /** * Set the default means of storage of unprotected and protected property values + * * @param strategy a property value strategy */ @Override - public void setPropertyValueStrategy(PropertyValue.Strategy strategy){ + public void setPropertyValueStrategy(PropertyValue.Strategy strategy) { this.valueStrategy = strategy; } @Override - public boolean supportsPropertyValueStrategy(){ + public boolean supportsPropertyValueStrategy() { return true; } } diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java index 894c926a..ad346f83 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java @@ -44,7 +44,7 @@ default void setup() throws Exception { default void testShouldProtect() { String propertyName = PASSWORD; assertThrows(UnsupportedOperationException.class, - () -> getDatabase().setShouldProtect(propertyName, true), + () -> getDatabase().setShouldProtect(propertyName, false), "By default the ProtectionStrategy should be immutable"); assertTrue(getDatabase().shouldProtect(propertyName)); } From c581da141d5bfeba24b448d69b79f63e3f8b80f5 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Wed, 12 Mar 2025 19:24:22 +0000 Subject: [PATCH 43/66] major refactor and removal of modules --- basic/pom.xml | 6 - .../pwdb/basic/BasicDatabase.java | 5 +- .../pwdb/basic/BasicDatabaseTest.java | 28 +- database/pom.xml | 5 - .../pwdb/test/BinaryPropertiesTest.java | 174 +++++++++++ .../pwdb/test/KdbxFileSaveAndReloadTest.java | 82 +++-- .../pwdb/test/ProtectedPropertyTest2.java | 151 ++++++++++ .../pwdb/test/RecycleBinTest.java | 4 +- .../linguafranca/pwdb/test/Test123Test.java | 65 ++-- .../pwdb/test/TrivialDatabaseTest.java | 9 +- .../linguafranca/util/MavenRunListener.java | 0 .../java/org/linguafranca/util/TestUtil.java | 2 +- example/pom.xml | 6 - .../linguafranca/pwdb/kdbx/ChooseFile.java | 8 +- .../java/org/linguafranca/pwdb/kdbx/Misc.java | 46 +-- .../linguafranca/pwdb/kdbx/OpenDbExample.java | 8 +- .../linguafranca/pwdb/kdbx/SaxParseTest.java | 6 +- .../pwdb/kdbx/SimpleQuickStartTest.java | 6 +- .../src/test/resources/V4-AES-AES.kdbx | Bin .../V4-ChaCha20-Argon2-Attachment.kdbx | Bin .../src/test/resources/test1.kdbx | Bin .../src/test/resources/test123.kdb | Bin .../src/test/resources/test123.kdbx | Bin kdb/pom.xml | 5 +- ...atabaseLoaderTest.java => Kdb123Test.java} | 23 +- .../pwdb/kdb/KdbDatabaseTest.java | 28 +- .../pwdb/kdb/KdbSerializerTest.java | 1 - {test => kdb}/src/test/resources/kdb.key | 0 .../src/test/resources/kdbwithkey.kdb | Bin kdb/src/test/resources/test123.kdb | Bin 0 -> 3148 bytes kdbx-database/pom.xml | 2 +- .../pwdb/kdbx/jackson/KdbxDatabase.java | 4 +- .../pwdb/kdbx/jackson/util}/Util.java | 8 +- .../pwdb/JacksonBinaryPropertyV3Test.java | 67 ----- .../pwdb/JacksonBinaryPropertyV4Test.java | 67 ----- .../pwdb/JacksonPropertyValueTest.java | 52 ---- .../linguafranca/pwdb/KdbxDatabaseTest.java | 36 --- .../database/Kdbx123Test.java} | 38 ++- .../kdbx/database/KdbxBinariesV3Test.java | 90 ++++++ .../database/KdbxBinariesV4Test.java} | 15 +- .../database}/KdbxDatabaseLoadTest.java | 12 +- .../pwdb/kdbx/database/KdbxDatabaseTest.java | 85 ++++++ .../database/KdbxSaveAndReloadTest.java} | 70 +++-- .../pwdb/kdbx/database/util}/UtilTest.java | 15 +- .../database}/validation/Issue27Test.java | 10 +- .../database}/validation/Issue33Test.java | 8 +- .../database}/validation/Issue38Test.java | 10 +- .../database}/validation/Issue70Test.java | 10 +- .../src/test/resources/Attachment.kdbx | Bin .../src/test/resources/Attachment.xml | 0 .../src/test/resources/Database-4.1-123.kdbx | Bin .../test/resources}/Database/Database.kdbx | Bin .../test/resources}/Database/Database.keyx | 0 .../src/test/resources}/Database/README.md | 0 .../src/test/resources/ExampleDatabase.kdbx | Bin .../src/test/resources/ExampleDatabase.xml | 0 .../src/test/resources/KeyFileDatabase.kdbx | Bin .../src/test/resources/V3-CustomIcon.kdbx | Bin .../src/test/resources/V4-AES-AES.kdbx | Bin 0 -> 2055 bytes .../resources/V4-AES-Argon2-CustomIcon.kdbx | Bin .../src/test/resources/V4-AES-Argon2.kdbx | Bin .../src/test/resources/V4-ChaCha20-AES.kdbx | Bin .../V4-ChaCha20-Argon2-Attachment.kdbx | Bin 0 -> 9842 bytes .../src/test/resources/V4-CustomIcon.kdbx | Bin .../resources/issue-27/bogus-timestamp.kdbx | Bin .../resources/issue-27/bogus-timestamp2.kdbx | Bin .../src/test/resources/issue-38/Database.kdbx | Bin 0 -> 1870 bytes .../src/test/resources/issue-38/Database.keyx | 12 + .../src/test/resources/issue-38/README.md | 1 + .../test/resources/issue-70/test-hugoo10.kdbx | Bin .../src/test/resources/issue-70/test1.kdbx | Bin .../src/test/resources/issue-70/test2.kdbx | Bin .../src/test/resources/kdbxSample.xml | 0 .../resources/kdbx_hash_test_wrong_hash.keyx | 0 .../src/test/resources/letter J.jpeg | Bin .../src/test/resources/letter L.jpeg | Bin kdbx-database/src/test/resources/test1.kdbx | Bin 0 -> 2362 bytes .../src/test/resources/test123-AES-AES.kdbx | Bin .../test/resources/test123-AES-Argon2.kdbx | Bin .../test/resources/test123-ChaCha20-AES.kdbx | Bin .../resources/test123-ChaCha20-Argon2.kdbx | Bin kdbx-database/src/test/resources/test123.kdbx | Bin 0 -> 3486 bytes .../src/test/resources/testDeleted.kdbx | Bin .../src/test/resources/xml/V4-AES-AES.xml | 0 .../src/test/resources/xml/V4-AES-Argon2.xml | 0 .../test/resources/xml/V4-ChaCha20-AES.xml | 0 .../xml/V4-ChaCha20-Argon2-Attachment.xml | 0 kdbx-io/pom.xml | 2 +- .../pwdb/kdbx/KdbxHeaderTest.java | 4 +- .../pwdb/kdbx/KdbxKeyFileTest.java | 20 +- .../pwdb/kdbx/KdbxSerializerTest.java | 10 +- kdbx-io/src/test/resources/Attachment.kdbx | Bin 0 -> 10126 bytes .../src/test/resources/EmptyPassword.kdbx | Bin .../test/resources/EmptyPasswordWithKey.kdbx | Bin .../test/resources/EmptyPasswordWithKey.key | 0 .../src/test/resources/KeyFileDatabase.key | 0 .../src/test/resources/NoPasswordWithKey.kdbx | Bin .../src/test/resources/NoPasswordWithKey.key | 0 kdbx-io/src/test/resources/V4-AES-AES.kdbx | Bin 0 -> 2055 bytes kdbx-io/src/test/resources/V4-AES-Argon2.kdbx | Bin 0 -> 2005 bytes .../src/test/resources/V4-ChaCha20-AES.kdbx | Bin 0 -> 2039 bytes .../V4-ChaCha20-Argon2-Attachment.kdbx | Bin 0 -> 9842 bytes .../test/resources/kdb_with_random_file.kdbx | Bin .../src/test/resources/kdbx_hash_test.kdbx | Bin .../src/test/resources/kdbx_hash_test.keyx | 0 .../src/test/resources/kdbx_keyfile32.kdbx | Bin .../src/test/resources/kdbx_keyfile64.kdbx | Bin .../src/test/resources/keyfile32 | 0 .../src/test/resources/keyfile64 | 0 .../src/test/resources/random_file | Bin pom.xml | 2 - test/pom.xml | 58 ---- .../pwdb/checks/BasicDatabaseChecks.java | 282 ------------------ .../pwdb/checks/BinaryPropertyChecks.java | 207 ------------- .../pwdb/checks/PropertyValueChecks.java | 188 ------------ .../pwdb/checks/RecycleBinChecks.java | 95 ------ util/pom.xml | 51 ---- 117 files changed, 823 insertions(+), 1376 deletions(-) create mode 100644 database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java rename test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java => database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java (80%) create mode 100644 database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java rename test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java => database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java (58%) rename {util/src/main => database/src/test}/java/org/linguafranca/util/MavenRunListener.java (100%) rename {util/src/main => database/src/test}/java/org/linguafranca/util/TestUtil.java (99%) rename util/src/main/java/org/linguafranca/util/HexViewer.java => example/src/main/java/org/linguafranca/pwdb/kdbx/Misc.java (50%) rename {test => example}/src/test/resources/V4-AES-AES.kdbx (100%) rename {test => example}/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx (100%) rename {test => example}/src/test/resources/test1.kdbx (100%) rename {test => example}/src/test/resources/test123.kdb (100%) rename {test => example}/src/test/resources/test123.kdbx (100%) rename kdb/src/test/java/org/linguafranca/pwdb/kdb/{KdbDatabaseLoaderTest.java => Kdb123Test.java} (69%) rename {test => kdb}/src/test/resources/kdb.key (100%) rename {test => kdb}/src/test/resources/kdbwithkey.kdb (100%) create mode 100755 kdb/src/test/resources/test123.kdb rename {example/src/main/java/org/linguafranca/pwdb/kdbx => kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util}/Util.java (94%) delete mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java delete mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java delete mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java delete mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java rename kdbx-database/src/test/java/org/linguafranca/pwdb/{KdbxDatabaseLoaderTest.java => kdbx/database/Kdbx123Test.java} (53%) create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java rename kdbx-database/src/test/java/org/linguafranca/pwdb/{JacksonRecycleBinTest.java => kdbx/database/KdbxBinariesV4Test.java} (60%) rename kdbx-database/src/test/java/org/linguafranca/pwdb/{ => kdbx/database}/KdbxDatabaseLoadTest.java (82%) create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java rename kdbx-database/src/test/java/org/linguafranca/pwdb/{JacksonSaveAndReloadTest.java => kdbx/database/KdbxSaveAndReloadTest.java} (55%) rename {example/src/test/java/org/linguafranca/pwdb/kdbx => kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util}/UtilTest.java (78%) rename {example/src/test/java/org/linguafranca/pwdb/kdbx => kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database}/validation/Issue27Test.java (89%) rename {example/src/test/java/org/linguafranca/pwdb/kdbx => kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database}/validation/Issue33Test.java (88%) rename {example/src/test/java/org/linguafranca/pwdb/kdbx => kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database}/validation/Issue38Test.java (83%) rename {example/src/test/java/org/linguafranca/pwdb/kdbx => kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database}/validation/Issue70Test.java (83%) rename {test => kdbx-database}/src/test/resources/Attachment.kdbx (100%) rename {test => kdbx-database}/src/test/resources/Attachment.xml (100%) rename {test => kdbx-database}/src/test/resources/Database-4.1-123.kdbx (100%) rename {test/src/test/resources/issue-38 => kdbx-database/src/test/resources}/Database/Database.kdbx (100%) rename {test/src/test/resources/issue-38 => kdbx-database/src/test/resources}/Database/Database.keyx (100%) rename {test/src/test/resources/issue-38 => kdbx-database/src/test/resources}/Database/README.md (100%) rename {test => kdbx-database}/src/test/resources/ExampleDatabase.kdbx (100%) rename {test => kdbx-database}/src/test/resources/ExampleDatabase.xml (100%) rename {test => kdbx-database}/src/test/resources/KeyFileDatabase.kdbx (100%) rename {test => kdbx-database}/src/test/resources/V3-CustomIcon.kdbx (100%) create mode 100644 kdbx-database/src/test/resources/V4-AES-AES.kdbx rename {test => kdbx-database}/src/test/resources/V4-AES-Argon2-CustomIcon.kdbx (100%) rename {test => kdbx-database}/src/test/resources/V4-AES-Argon2.kdbx (100%) rename {test => kdbx-database}/src/test/resources/V4-ChaCha20-AES.kdbx (100%) create mode 100755 kdbx-database/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx rename {test => kdbx-database}/src/test/resources/V4-CustomIcon.kdbx (100%) rename {test => kdbx-database}/src/test/resources/issue-27/bogus-timestamp.kdbx (100%) rename {test => kdbx-database}/src/test/resources/issue-27/bogus-timestamp2.kdbx (100%) create mode 100644 kdbx-database/src/test/resources/issue-38/Database.kdbx create mode 100644 kdbx-database/src/test/resources/issue-38/Database.keyx create mode 100644 kdbx-database/src/test/resources/issue-38/README.md rename {test => kdbx-database}/src/test/resources/issue-70/test-hugoo10.kdbx (100%) rename {test => kdbx-database}/src/test/resources/issue-70/test1.kdbx (100%) rename {test => kdbx-database}/src/test/resources/issue-70/test2.kdbx (100%) rename {test => kdbx-database}/src/test/resources/kdbxSample.xml (100%) rename {test => kdbx-database}/src/test/resources/kdbx_hash_test_wrong_hash.keyx (100%) rename {test => kdbx-database}/src/test/resources/letter J.jpeg (100%) rename {test => kdbx-database}/src/test/resources/letter L.jpeg (100%) create mode 100644 kdbx-database/src/test/resources/test1.kdbx rename {test => kdbx-database}/src/test/resources/test123-AES-AES.kdbx (100%) rename {test => kdbx-database}/src/test/resources/test123-AES-Argon2.kdbx (100%) rename {test => kdbx-database}/src/test/resources/test123-ChaCha20-AES.kdbx (100%) rename {test => kdbx-database}/src/test/resources/test123-ChaCha20-Argon2.kdbx (100%) create mode 100644 kdbx-database/src/test/resources/test123.kdbx rename {test => kdbx-database}/src/test/resources/testDeleted.kdbx (100%) rename {test => kdbx-database}/src/test/resources/xml/V4-AES-AES.xml (100%) rename {test => kdbx-database}/src/test/resources/xml/V4-AES-Argon2.xml (100%) rename {test => kdbx-database}/src/test/resources/xml/V4-ChaCha20-AES.xml (100%) rename {test => kdbx-database}/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml (100%) create mode 100644 kdbx-io/src/test/resources/Attachment.kdbx rename {test => kdbx-io}/src/test/resources/EmptyPassword.kdbx (100%) rename {test => kdbx-io}/src/test/resources/EmptyPasswordWithKey.kdbx (100%) rename {test => kdbx-io}/src/test/resources/EmptyPasswordWithKey.key (100%) rename {test => kdbx-io}/src/test/resources/KeyFileDatabase.key (100%) rename {test => kdbx-io}/src/test/resources/NoPasswordWithKey.kdbx (100%) rename {test => kdbx-io}/src/test/resources/NoPasswordWithKey.key (100%) create mode 100644 kdbx-io/src/test/resources/V4-AES-AES.kdbx create mode 100755 kdbx-io/src/test/resources/V4-AES-Argon2.kdbx create mode 100644 kdbx-io/src/test/resources/V4-ChaCha20-AES.kdbx create mode 100755 kdbx-io/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx rename {test => kdbx-io}/src/test/resources/kdb_with_random_file.kdbx (100%) rename {test => kdbx-io}/src/test/resources/kdbx_hash_test.kdbx (100%) rename {test => kdbx-io}/src/test/resources/kdbx_hash_test.keyx (100%) rename {test => kdbx-io}/src/test/resources/kdbx_keyfile32.kdbx (100%) rename {test => kdbx-io}/src/test/resources/kdbx_keyfile64.kdbx (100%) rename {test => kdbx-io}/src/test/resources/keyfile32 (100%) rename {test => kdbx-io}/src/test/resources/keyfile64 (100%) rename {test => kdbx-io}/src/test/resources/random_file (100%) delete mode 100644 test/pom.xml delete mode 100644 test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java delete mode 100644 test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java delete mode 100644 test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java delete mode 100644 test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java delete mode 100644 util/pom.xml diff --git a/basic/pom.xml b/basic/pom.xml index d9f27278..982b9afd 100644 --- a/basic/pom.xml +++ b/basic/pom.xml @@ -57,12 +57,6 @@ ${project.version} test-jar - - org.linguafranca.pwdb - test - ${project.version} - test-jar - \ No newline at end of file diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 4502fe33..96f37d39 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -23,6 +23,7 @@ import java.io.IOException; import java.io.OutputStream; +import java.time.Instant; import java.util.Stack; /** @@ -30,8 +31,8 @@ */ public class BasicDatabase extends ProtectedDatabase { private final BasicGroup root = new BasicGroup(this, "Root"); - private String databaseName; - private String databaseDescription; + private String databaseName = "New Database"; + private String databaseDescription = "Database created on " + Instant.now().toString(); @Override public Group getRootGroup() { diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java index 73376e7b..c7e7924a 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java @@ -17,17 +17,20 @@ package org.linguafranca.pwdb.basic; +import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.test.GroupsAndEntriesTest; -import org.linguafranca.pwdb.test.RecycleBinTest; -import org.linguafranca.pwdb.test.ProtectedPropertyTest; -import org.linguafranca.pwdb.test.TrivialDatabaseTest; +import org.linguafranca.pwdb.test.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; public class BasicDatabaseTest implements TrivialDatabaseTest, GroupsAndEntriesTest, RecycleBinTest, - ProtectedPropertyTest { + ProtectedPropertyTest, + ProtectedPropertyTest2 { Database database; @@ -56,4 +59,19 @@ public Database getDatabase() { } + @Override + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { + new BasicDatabaseSerializer.Xml().save((BasicDatabase) database, outputStream); + } + + @Override + public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ + return new BasicDatabaseSerializer.Xml().load(inputStream); + } + + @Override + public Credentials getCredentials(byte[] credentials){ + return new Credentials.None(); + } + } diff --git a/database/pom.xml b/database/pom.xml index 6f833178..319d3f20 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -30,11 +30,6 @@ Contains root definitions of PWDB Databases etc. - - org.linguafranca.pwdb - util - ${project.version} - com.google.guava guava diff --git a/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java new file mode 100644 index 00000000..4e676ca8 --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.test; + +import com.google.common.io.ByteStreams; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Entry; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.*; + + +public interface BinaryPropertiesTest { + + void newDatabase(); + Database getDatabase(); + Database createDatabase(); + void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; + Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; + Credentials getCredentials(byte[] credentials); + + /** + * Verify that the database correctly reports that its supports binary attachments - override for + * databases that don't + */ + @Test + default void checkSupported() { + assertTrue(getDatabase().supportsBinaryProperties()); + } + + /** + * Retrieve and verify attachment "letter J" + */ + @Test + default void getBinaryProperty() throws Exception { + Entry entry = getDatabase().findEntries("Test attachment").get(0); + byte[] letterJ = entry.getBinaryProperty("letter J.jpeg"); + InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); + assert testFile != null; + byte[] original = ByteStreams.toByteArray(testFile); + assertArrayEquals(original, letterJ); + } + + /** + * Retrieve and verify attachment "letter L" + */ + @Test + default void getAnotherBinaryProperty() throws Exception { + Entry entry = getDatabase().findEntries("Test 2 attachment").get(0); + byte[] letterL = entry.getBinaryProperty("letter L.jpeg"); + InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); + assert testFile != null; + byte[] original = ByteStreams.toByteArray(testFile); + assertArrayEquals(original, letterL); + } + + /** + * Add the Letter L to the entry containing letter J + */ + @Test + default void setBinaryProperty() throws Exception { + InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); + assert testFile != null; + byte[] original = ByteStreams.toByteArray(testFile); + Entry entry = getDatabase().findEntries("Test attachment").get(0); + entry.setBinaryProperty("letter L.jpeg", original); + byte[] letterL = entry.getBinaryProperty("letter L.jpeg"); + assertArrayEquals(original, letterL); + assertArrayEquals(new String[]{"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); + assertTrue(getDatabase().isDirty()); + } + + /** + * Verify that the entries have the right attachments + */ + @Test + default void getBinaryPropertyNames() { + Entry entry = getDatabase().findEntries("Test attachment").get(0); + assertArrayEquals(new String[]{"letter J.jpeg"}, entry.getBinaryPropertyNames().toArray()); + + entry = getDatabase().findEntries("Test 2 attachment").get(0); + assertArrayEquals(new String[]{"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); + } + + /** + * Verify that the binary properties can be added and removed correctly + */ + @Test + default void checkAddChangeRemoveBinaryProperty() { + byte[] test = new byte[]{0, 1, 2, 3}; + byte[] test2 = new byte[]{3, 2, 1, 0}; + Entry entry = getDatabase().findEntries("Test attachment").get(0); + assertEquals(1, entry.getBinaryPropertyNames().size()); + entry.setBinaryProperty("test", test); + assertArrayEquals(test, entry.getBinaryProperty("test")); + entry.setBinaryProperty("test", test2); + assertArrayEquals(test2, entry.getBinaryProperty("test")); + // true that property was removed + assertTrue(entry.removeBinaryProperty("test")); + // false that same property was removed + assertFalse(entry.removeBinaryProperty("test")); + // false that non-existent was removed + assertFalse(entry.removeBinaryProperty("test-test")); + // same number of properties as we started with + assertEquals(1, entry.getBinaryPropertyNames().size()); + } + + /** + * Checks that a database with binary properties saves and reloads correctly + */ + @Test + default void saveAndReloadCheck() throws IOException { + Path file = Files.createTempFile("test8", "tmp"); + saveDatabase(getDatabase(), getCredentials("123".getBytes()), Files.newOutputStream(file)); + + Database db = loadDatabase(getCredentials("123".getBytes()), Files.newInputStream(file)); + Entry newEntry = db.findEntries("Test attachment").get(0); + assertArrayEquals(new String[]{"letter J.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); + Entry oldEntry = getDatabase().findEntries("Test attachment").get(0); + assertArrayEquals(oldEntry.getBinaryProperty("letter J.jpeg"), newEntry.getBinaryProperty("letter J.jpeg")); + + newEntry = db.findEntries("Test 2 attachment").get(0); + assertArrayEquals(new String[]{"letter J.jpeg", "letter L.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); + oldEntry = getDatabase().findEntries("Test 2 attachment").get(0); + assertArrayEquals(oldEntry.getBinaryProperty("letter J.jpeg"), newEntry.getBinaryProperty("letter J.jpeg")); + assertArrayEquals(oldEntry.getBinaryProperty("letter L.jpeg"), newEntry.getBinaryProperty("letter L.jpeg")); + + } + + /** + * Checks that a new database can add binary properties saves and reloads correctly + */ + @Test + default void createAndSaveCheck() throws IOException { + Path file = Files.createTempFile("keepass", "tmp"); + Database database1 = createDatabase(); + Entry entry = database1.newEntry("Test attachment"); + database1.getRootGroup().addEntry(entry); + InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); + assert testFile != null; + byte[] letterJ = ByteStreams.toByteArray(testFile); + entry.setBinaryProperty("letter J.jpeg", letterJ); + saveDatabase(database1, getCredentials("123".getBytes()), Files.newOutputStream(file)); + + Database db = loadDatabase(getCredentials("123".getBytes()), Files.newInputStream(file)); + Entry entry1 = db.findEntries("Test attachment").get(0); + // just one property + assertArrayEquals(new String[]{"letter J.jpeg"}, entry1.getBinaryPropertyNames().toArray()); + // content is correct + assertArrayEquals(letterJ, entry1.getBinaryProperty("letter J.jpeg")); + } +} diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java similarity index 80% rename from test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java rename to database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java index e4064799..682dce7f 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/SaveAndReloadChecks.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java @@ -4,9 +4,9 @@ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.checks;/* +package org.linguafranca.pwdb.test;/* * Copyright 2015 Jo Rabin * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -31,9 +31,9 @@ * limitations under the License. */ -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.*; import java.io.*; @@ -41,35 +41,31 @@ import java.nio.file.Paths; import java.util.UUID; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; + /** * @author jo */ -public abstract class SaveAndReloadChecks { - - static PrintStream printStream = getTestPrintStream(); +public interface KdbxFileSaveAndReloadTest { - public abstract Database getDatabase(); - public abstract Database getDatabase(String name, Credentials credentials) throws IOException; + void newDatabase(); + Database getDatabase(); + Database createDatabase(); + void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; + Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; + Credentials getCredentials(byte[] credentials); + boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2); - public abstract void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; - public abstract Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; - public abstract Credentials getCreds(byte[] creds); - public abstract boolean verifyStreamFormat (StreamFormat s1, StreamFormat s2); - @BeforeClass - public static void ensureOutputDir() throws IOException { - Files.createDirectories(Paths.get("testOutput")); - } /** * Test verifies that entries contain the same content on reload as they did on save, * and also verifies that saving doesn't alter the contents */ @Test - public void saveAndReloadTest() throws IOException { + default void saveAndReloadTest() throws IOException { long now = System.currentTimeMillis(); @@ -79,7 +75,7 @@ public void saveAndReloadTest() throws IOException { //output.save(new StreamFormat.None(), new Credentials.None(), printStream); FileOutputStream fos = new FileOutputStream("testOutput/test1.kdbx"); - saveDatabase(output, getCreds("123".getBytes()), fos); + saveDatabase(output, getCredentials("123".getBytes()), fos); assertFalse(output.isDirty()); fos.flush(); fos.close(); @@ -89,18 +85,19 @@ public void saveAndReloadTest() throws IOException { FileInputStream fis = new FileInputStream("testOutput/test1.kdbx"); - Database input = loadDatabase(getCreds("123".getBytes()), fis); + Database input = loadDatabase(getCredentials("123".getBytes()), fis); verifyContents(input); //input.save(new StreamFormat.None(), new Credentials.None(), printStream); - printStream.format("Test took %d millis", System.currentTimeMillis() - now); + //getTestPrintStream().format("Test took %d millis", System.currentTimeMillis() - now); } /** * Test verifies that attachments are saved and reloaded correctly */ @Test - public void saveAndReloadTest2() throws IOException { - Database attachment = getDatabase("Attachment.kdbx", getCreds("123".getBytes())); + default void saveAndReloadTest2() throws IOException { + Database attachment = loadDatabase(getCredentials("123".getBytes()), + getClass().getClassLoader().getResourceAsStream("Attachment.kdbx" )); Entry entry = attachment.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg"}, entry.getBinaryPropertyNames().toArray()); @@ -113,12 +110,12 @@ public void saveAndReloadTest2() throws IOException { assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); FileOutputStream fos = new FileOutputStream("testOutput/test2.kdbx"); - saveDatabase(attachment, getCreds("123".getBytes()), fos); + saveDatabase(attachment, getCredentials("123".getBytes()), fos); fos.flush(); fos.close(); FileInputStream fis = new FileInputStream("testOutput/test2.kdbx"); - Database input = loadDatabase(getCreds("123".getBytes()), fis); + Database input = loadDatabase(getCredentials("123".getBytes()), fis); entry = input.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); @@ -135,22 +132,23 @@ public void saveAndReloadTest2() throws IOException { * Test verifies that database is saved with same encryption that it was loaded with */ @Test - public void saveAndReloadTest3() throws IOException { + default void saveAndReloadTest3() throws IOException { for (String resource: testFiles) { - Database database = getDatabase(resource, this.getCreds("123".getBytes())); + Database database = loadDatabase(this.getCredentials("123".getBytes()), + getClass().getClassLoader().getResourceAsStream(resource)); StreamFormat format1 = database.getStreamFormat(); - database.save(getCreds("123".getBytes()), Files.newOutputStream(Paths.get("testOutput/test3.kdbx"))); + database.save(getCredentials("123".getBytes()), Files.newOutputStream(Paths.get("testOutput/test3.kdbx"))); FileInputStream fis = new FileInputStream("testOutput/test3.kdbx"); - Database input = loadDatabase(getCreds("123".getBytes()), fis); + Database input = loadDatabase(getCredentials("123".getBytes()), fis); StreamFormat format2 = input.getStreamFormat(); assertTrue(verifyStreamFormat(format1, format2)); } } private Database createNewDatabase() throws IOException { - Database database = getDatabase(); + Database database = createDatabase(); for (@SuppressWarnings("WrapperTypeMayBePrimitive") Integer g = 0; g < 5; g++){ Group group = database.getRootGroup().addGroup(database.newGroup(g.toString())); @@ -196,30 +194,30 @@ private void verifyContents(Database database) { * * @throws IOException when naughty */ - @Test @Ignore - public void saveNewDatabase () throws IOException { + @Disabled + default void saveNewDatabase () throws IOException { Database database = createNewDatabase(); FileOutputStream outputStream = new FileOutputStream("compatibility.kdbx"); - saveDatabase(database, getCreds("123".getBytes()), outputStream); + saveDatabase(database, getCredentials("123".getBytes()), outputStream); } /** * Doesn't do anything other than output the database using default PrintVisitor * @throws IOException when naughty */ - @Test @Ignore - public void inspectNewDatabase () throws IOException { + @Disabled + default void inspectNewDatabase () throws IOException { Database database = createNewDatabase(); - database.visit(new Visitor.Print(printStream)); + database.visit(new Visitor.Print(getTestPrintStream())); } // create a new database for messing around with - // the assertions here somewhat duplicate those in BasicDatabaseChecks + // the assertions here somewhat duplicate those elsewhere @Test - public void testNewDatabase() throws IOException { - Database database = getDatabase(); + default void testNewDatabase() throws IOException { + Database database = createDatabase(); Group root = database.getRootGroup(); assertTrue(root.isRootGroup()); assertEquals(0, root.getGroups().size()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java new file mode 100644 index 00000000..2f2d08ab --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.test; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.PropertyValue; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.List; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.*; + +public interface ProtectedPropertyTest2 { + + void newDatabase(); + Database getDatabase(); + void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; + Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; + Credentials getCredentials(byte[] credentials); + + @BeforeEach + default void ppt2SetUp() throws Exception { + newDatabase(); + } + + /** + * Catches expected thrown exception + * + * @param f a Consumer i.e. a function that does have output + * @param c the exception that is expected to be thrown when the function is executed + */ + private void catchException(Consumer f, @SuppressWarnings("SameParameterValue") Class c) { + try { + f.accept(null); + } catch (Exception e) { + if (c.equals(e.getClass())) { + return; + } + } + fail(c.getName() + " expected"); + } + + /** + * Check that unsupported operations throw exceptions + */ + @Test + default void noPropertyValueSupport() { + if (getDatabase().supportsPropertyValueStrategy()) return; + + assertFalse(getDatabase().supportsPropertyValueStrategy()); + // test a sample of the methods that are supposed to thrown if used here + catchException((z) -> getDatabase().setPropertyValueStrategy(null), UnsupportedOperationException.class); + catchException((z) -> getDatabase().getPropertyValueStrategy(), UnsupportedOperationException.class); + catchException((z) -> getDatabase().setShouldProtect(null, true), UnsupportedOperationException.class); + catchException((z) -> getDatabase().listShouldProtect(), UnsupportedOperationException.class); + + Entry entry = getDatabase().newEntry(); + catchException((z) -> entry.getPropertyValue(null), UnsupportedOperationException.class); + catchException((z) -> entry.setPropertyValue(null, (PropertyValue) null), UnsupportedOperationException.class); + + } + + @Test + default void checkDefaults(){ + // these are the defaults established by createDatabase() + assertTrue(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_PASSWORD)); + assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_USER_NAME)); + assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_NOTES)); + assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_URL)); + assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_TITLE)); + } + + @Test + default void expectedStorageType() throws IOException { + if (!getDatabase().supportsPropertyValueStrategy()) { + return; + } + + // Password is protected + PropertyValue.Strategy pvs = getDatabase().getPropertyValueStrategy(); + PropertyValue pv = pvs.getFactoryFor("Password").of("123"); + assertEquals(pv.getClass(), pvs.newProtected().of("a").getClass()); + assertTrue(pv.isProtected()); + + // Notes isn't protected + pv = pvs.getFactoryFor("Notes").of("123"); + assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); + assertFalse(pv.isProtected()); + + // "random" is not protected + pv = pvs.getFactoryFor("random").of("123"); + assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); + assertFalse(pv.isProtected()); + + // create a strategy that makes "random" protected + PropertyValue.Strategy newPvs = new PropertyValue.Strategy.MutableProtectionStrategy(); + getDatabase().setPropertyValueStrategy(newPvs); + getDatabase().setShouldProtect("random", true); + + // get a value for random and check it is protected + pv = newPvs.getFactoryFor("random").of("123"); + assertEquals(pv.getClass(), pvs.newProtected().of("b").getClass()); + assertTrue(pv.isProtected()); + + // create an entry with random property as protected value + Entry entry = getDatabase().newEntry("Test Random"); + entry.setPropertyValue("random", pv); + getDatabase().getRootGroup().addEntry(entry); + + // Save database + + FileOutputStream fos = new FileOutputStream("testOutput/test9.kdbx"); + saveDatabase(getDatabase(), getCredentials("123".getBytes()), fos); + fos.flush(); + fos.close(); + + // reload database, "random" is still protected even though it's not protected by default + FileInputStream fis = new FileInputStream("testOutput/test9.kdbx"); + Database input = loadDatabase(getCredentials("123".getBytes()), fis); + + List entries = input.findEntries("random"); + assertEquals(1, entries.size()); + assertTrue(entries.get(0).getPropertyValue("random").isProtected()); + assertFalse(input.getPropertyValueStrategy().getProtectedProperties().contains("random")); + } + + } + diff --git a/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java b/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java index 0249a563..f2b3ed49 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java @@ -41,7 +41,7 @@ default void makeDatabase() throws Exception { @Test default void recycleBinEntries() { - assumeTrue(getDatabase().supportsRecycleBin()); + assumeTrue(getDatabase().supportsRecycleBin(), "Database does not support recycle bin, skipping test"); Database database = getDatabase(); database.enableRecycleBin(false); assertFalse(database.isRecycleBinEnabled()); @@ -75,7 +75,7 @@ default void recycleBinEntries() { @Test default void recycleBinGroups() { - assumeTrue(getDatabase().supportsRecycleBin()); + assumeTrue(getDatabase().supportsRecycleBin(), "Database does not support recycle bin, skipping test"); Database database = getDatabase(); database.enableRecycleBin(true); assertTrue(database.isRecycleBinEnabled()); diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java similarity index 58% rename from test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java rename to database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java index 2cf90c57..d8ae6e30 100644 --- a/test/src/test/java/org/linguafranca/pwdb/checks/DatabaseLoaderChecks.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java @@ -15,63 +15,50 @@ * */ -package org.linguafranca.pwdb.checks;/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +package org.linguafranca.pwdb.test; -import org.junit.Assert; -import org.junit.Test; -import org.linguafranca.pwdb.*; +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Visitor; -import java.io.PrintStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** - * @author jo + * a test123 file for each format. Should contain the same thing. This is a basic sanity check. + *

+ * (i.e. there exist files called test123 for some formats and this test tests their contents. presumably + * back in the day, a database was created then saved in kdbx and kdb formats and when there was more than + * one KDBX implementation the test was created to test them all as well as the KDB implementation) */ -public abstract class DatabaseLoaderChecks { +public interface Test123Test { - static PrintStream printStream = getTestPrintStream(); + boolean getSkipDateCheck(); + Database getDatabase(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssX"); - protected Database database; - protected boolean skipDateCheck = false; - private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssX"); - /** - * a test123 file for each format. Should contain the same thing. This is a basic sanity check. - */ @Test - public void test123File() throws ParseException { + default void test123File() throws ParseException { // visit all groups and entries and list them to console - database.visit(new Visitor.Print(printStream)); + getDatabase().visit(new Visitor.Print(getTestPrintStream())); // find all entries in the database // the kdb version has three additional system related entries - List anything = database.findEntries(""); - Assert.assertTrue(10 <= anything.size()); + List anything = getDatabase().findEntries(""); + assertTrue(10 <= anything.size()); // find all entries in the database that have the string "test" in them - List tests = database.findEntries("test"); + List tests = getDatabase().findEntries("test"); for (Entry tes: tests) { - printStream.println(tes.getTitle()); + getTestPrintStream().println(tes.getTitle()); } assertEquals(4, tests.size()); if (tests.size() > 0) { @@ -88,14 +75,14 @@ public void test123File() throws ParseException { assertEquals("123", pass2); } - List passwords = database.findEntries("password"); + List passwords = getDatabase().findEntries("password"); assertEquals(4, passwords.size()); for (Entry passwordEntry : passwords) { assertEquals(passwordEntry.getTitle(), passwordEntry.getPassword()); - printStream.println(passwordEntry.getTitle()); + getTestPrintStream().println(passwordEntry.getTitle()); } - List entries = database.findEntries(new Entry.Matcher() { + List entries = getDatabase().findEntries(new Entry.Matcher() { @Override public boolean matches(Entry entry) { return entry.getTitle().equals("hello world"); @@ -105,7 +92,7 @@ public boolean matches(Entry entry) { assertEquals("pass", entries.get(0).getPassword()); // kdb files don't have a time zone so can't make head or tail of the date - test file seems to have a local time in it - if (skipDateCheck) { + if (getSkipDateCheck()) { return; } diff --git a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java index b06a4145..5f34bbe1 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java @@ -72,11 +72,6 @@ default void testNewIconWithInteger() { assertEquals(1, icon.getIndex()); } - @Test - default void testSupportsRecycleBin() { - assertFalse(getDatabase().supportsRecycleBin()); - } - @Test default void testRecycleBin() { if (getDatabase().supportsRecycleBin()) { @@ -92,14 +87,14 @@ default void testRecycleBin() { @Test default void testGetSetName() { - assertNull(getDatabase().getName()); + assertEquals("New Database", getDatabase().getName()); getDatabase().setName("Test Database"); assertEquals("Test Database", getDatabase().getName()); } @Test default void testGetSetDescription() { - assertNull(getDatabase().getDescription()); + assertNotNull(getDatabase().getDescription()); getDatabase().setDescription("Test Description"); assertEquals("Test Description", getDatabase().getDescription()); } diff --git a/util/src/main/java/org/linguafranca/util/MavenRunListener.java b/database/src/test/java/org/linguafranca/util/MavenRunListener.java similarity index 100% rename from util/src/main/java/org/linguafranca/util/MavenRunListener.java rename to database/src/test/java/org/linguafranca/util/MavenRunListener.java diff --git a/util/src/main/java/org/linguafranca/util/TestUtil.java b/database/src/test/java/org/linguafranca/util/TestUtil.java similarity index 99% rename from util/src/main/java/org/linguafranca/util/TestUtil.java rename to database/src/test/java/org/linguafranca/util/TestUtil.java index 1fbcd3a7..7d562ca0 100644 --- a/util/src/main/java/org/linguafranca/util/TestUtil.java +++ b/database/src/test/java/org/linguafranca/util/TestUtil.java @@ -37,4 +37,4 @@ public static PrintStream getTestPrintStream() { new PrintStream(new NullOutputStream()) : new PrintStream(System.out); } -} +} \ No newline at end of file diff --git a/example/pom.xml b/example/pom.xml index 97587d27..1da64fd3 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -52,12 +52,6 @@ ch.qos.logback logback-classic - - org.linguafranca.pwdb - test - ${project.version} - test-jar - org.linguafranca.pwdb database diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java index 651560dd..65e1eee9 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java @@ -18,7 +18,7 @@ package org.linguafranca.pwdb.kdbx; import com.google.common.base.Strings; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; import org.slf4j.Logger; @@ -31,8 +31,8 @@ import java.nio.file.Files; import java.nio.file.Paths; -import static org.linguafranca.pwdb.kdbx.Util.streamToString; -import static org.linguafranca.util.TestUtil.getTestPrintStream; +import static org.linguafranca.pwdb.kdbx.jackson.util.Util.streamToString; + /** * Utility to allow browsing of database files and listing content to console @@ -86,7 +86,7 @@ public String getDescription() { } logger.info("Opening {}", fc.getSelectedFile().getPath()); try (InputStream is = Files.newInputStream(Paths.get(fc.getSelectedFile().getPath()))) { - InputStream ss = KdbxSerializer.createUnencryptedInputStream(new KdbxCreds(s.getBytes()), new KdbxHeader(), is); + InputStream ss = KdbxSerializer.createUnencryptedInputStream(new KdbxCredentials(s.getBytes()), new KdbxHeader(), is); System.out.println(streamToString(ss)); System.out.println(); System.out.flush(); diff --git a/util/src/main/java/org/linguafranca/util/HexViewer.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/Misc.java similarity index 50% rename from util/src/main/java/org/linguafranca/util/HexViewer.java rename to example/src/main/java/org/linguafranca/pwdb/kdbx/Misc.java index 0f29c7fd..e1058435 100644 --- a/util/src/main/java/org/linguafranca/util/HexViewer.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/Misc.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.util; +package org.linguafranca.pwdb.kdbx; import java.io.IOException; import java.io.InputStream; @@ -23,28 +23,32 @@ import static org.linguafranca.util.TestUtil.getTestPrintStream; -/** - * Useful for e.g. viewing the raw file contents - */ -public class HexViewer { - - static PrintStream printStream = getTestPrintStream(); - - public static void list(InputStream is) throws IOException { - for (int i = 0; i < 32; i++) { - byte[] buf = new byte [16]; - is.read(buf); - StringBuilder sb = new StringBuilder(); - for (byte b: buf) { - sb.append(String.format("%02X ", b)); - } - sb.append(" "); - for (byte b : buf) { - sb.append(b < 0x20 || b > 0x7e ? (char) 0x00B7 : (char) b); +public class Misc { + + /** + * Useful for e.g. viewing the raw file contents + */ + public static class HexViewer { + + static PrintStream printStream = getTestPrintStream(); + + public static void list(InputStream is) throws IOException { + for (int i = 0; i < 32; i++) { + byte[] buf = new byte [16]; + is.read(buf); + StringBuilder sb = new StringBuilder(); + for (byte b: buf) { + sb.append(String.format("%02X ", b)); + } + sb.append(" "); + for (byte b : buf) { + sb.append(b < 0x20 || b > 0x7e ? (char) 0x00B7 : (char) b); + } + printStream.println(sb); } - printStream.println(sb); } } -} + +} diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index f32ed3d4..06838e74 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -19,7 +19,7 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Visitor; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.InputStream; @@ -39,18 +39,18 @@ public class OpenDbExample { static PrintStream printStream = getTestPrintStream(); private interface DbLoader { - Database load(KdbxCreds creds, InputStream inputStream) throws Exception; + Database load(KdbxCredentials creds, InputStream inputStream) throws Exception; } private static class JacksonDbLoader implements DbLoader { @Override - public Database load(KdbxCreds creds, InputStream inputStream) throws Exception { + public Database load(KdbxCredentials creds, InputStream inputStream) throws Exception { return KdbxDatabase.load(creds, inputStream); } } private static void testDb (DbLoader loader, String label, int loads, int iterations) throws Exception { - KdbxCreds creds = new KdbxCreds("123".getBytes()); + KdbxCredentials creds = new KdbxCredentials("123".getBytes()); long start = System.currentTimeMillis(); for (int i=0; i < loads; i++) { try (InputStream inputStream = OpenDbExample.class.getClassLoader().getResourceAsStream("test1.kdbx")) { diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java index 3ab56a6f..c4d153c9 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java @@ -18,7 +18,7 @@ package org.linguafranca.pwdb.kdbx; import org.junit.Test; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.xml.sax.SAXException; import javax.xml.parsers.ParserConfigurationException; @@ -37,11 +37,11 @@ public class SaxParseTest extends SaxParse { OutputStream outputStream = getTestPrintStream(); @Test public void exampleSaxParsingV3 () throws IOException, SAXException, ParserConfigurationException { - super.exampleSaxParsing("test123.kdbx", new KdbxCreds("123".getBytes()), new PrintWriter(outputStream)); + super.exampleSaxParsing("test123.kdbx", new KdbxCredentials("123".getBytes()), new PrintWriter(outputStream)); } @Test public void exampleSaxParsingV4 () throws IOException, SAXException, ParserConfigurationException { - super.exampleSaxParsing("V4-AES-AES.kdbx", new KdbxCreds("123".getBytes()), new PrintWriter(outputStream)); + super.exampleSaxParsing("V4-AES-AES.kdbx", new KdbxCredentials("123".getBytes()), new PrintWriter(outputStream)); } } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index b856804f..8e7c5269 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -19,7 +19,7 @@ import org.junit.BeforeClass; import org.junit.Test; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.security.Encryption; @@ -66,7 +66,7 @@ public void loadSave() throws IOException { loadKdbx3SaveKdbx4("test123.kdbx","123".getBytes(), path); // load newly created V4 database - KdbxDatabase db = KdbxDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); + KdbxDatabase db = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), Files.newInputStream(path)); KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(4, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.CHA_CHA_20, streamFormat.getStreamConfiguration().getCipherAlgorithm()); @@ -80,7 +80,7 @@ public void loadSave2() throws IOException { loadKdbx4SaveKdbx3("V4-ChaCha20-Argon2-Attachment.kdbx","123".getBytes(), path); // load newly created V4 database - KdbxDatabase db = KdbxDatabase.load(new KdbxCreds("123".getBytes()), Files.newInputStream(path)); + KdbxDatabase db = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), Files.newInputStream(path)); KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(3, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.AES, streamFormat.getStreamConfiguration().getCipherAlgorithm()); diff --git a/test/src/test/resources/V4-AES-AES.kdbx b/example/src/test/resources/V4-AES-AES.kdbx similarity index 100% rename from test/src/test/resources/V4-AES-AES.kdbx rename to example/src/test/resources/V4-AES-AES.kdbx diff --git a/test/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx b/example/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx similarity index 100% rename from test/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx rename to example/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx diff --git a/test/src/test/resources/test1.kdbx b/example/src/test/resources/test1.kdbx similarity index 100% rename from test/src/test/resources/test1.kdbx rename to example/src/test/resources/test1.kdbx diff --git a/test/src/test/resources/test123.kdb b/example/src/test/resources/test123.kdb similarity index 100% rename from test/src/test/resources/test123.kdb rename to example/src/test/resources/test123.kdb diff --git a/test/src/test/resources/test123.kdbx b/example/src/test/resources/test123.kdbx similarity index 100% rename from test/src/test/resources/test123.kdbx rename to example/src/test/resources/test123.kdbx diff --git a/kdb/pom.xml b/kdb/pom.xml index fff80ce3..29a3cb80 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -27,7 +27,7 @@ KeePassJava2.kdb KeePassJava2 :: KDB - Contains an implementaion that can load KDB files. + Contains an implementation that can load KDB files. @@ -35,9 +35,10 @@ database ${project.version} + org.linguafranca.pwdb - test + database ${project.version} test-jar test diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseLoaderTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/Kdb123Test.java similarity index 69% rename from kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseLoaderTest.java rename to kdb/src/test/java/org/linguafranca/pwdb/kdb/Kdb123Test.java index 70a18b75..426924d6 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseLoaderTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/Kdb123Test.java @@ -18,7 +18,8 @@ package org.linguafranca.pwdb.kdb; import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.checks.DatabaseLoaderChecks; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.test.Test123Test; import java.io.IOException; import java.io.InputStream; @@ -26,15 +27,25 @@ /** * @author jo */ -public class KdbDatabaseLoaderTest extends DatabaseLoaderChecks { +public class Kdb123Test implements Test123Test { - public KdbDatabaseLoaderTest() throws IOException { - // get an input stream from KDB file + KdbDatabase kdbDatabase; + + Kdb123Test() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); // file has password credentials Credentials credentials = new KdbCredentials.Password("123".getBytes()); // open database. - super.database = KdbDatabase.load(credentials, inputStream); - super.skipDateCheck = true; + kdbDatabase = KdbDatabase.load(credentials, inputStream); + } + + @Override + public Database getDatabase() { + return kdbDatabase; + } + + @Override + public boolean getSkipDateCheck() { + return true; } } diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java index 4c23ea4d..9604027b 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java @@ -18,8 +18,8 @@ package org.linguafranca.pwdb.kdb; import org.junit.Test; -import org.linguafranca.pwdb.checks.BasicDatabaseChecks; import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.test.GroupsAndEntriesTest; import java.io.IOException; @@ -28,20 +28,32 @@ /** * @author Jo */ -public class KdbDatabaseTest extends BasicDatabaseChecks { +public class KdbDatabaseTest implements GroupsAndEntriesTest { + + KdbDatabase kdbDatabase; + + {newDatabase();} + @Override - public Database createDatabase() throws IOException { + public KdbDatabase createDatabase() { return new KdbDatabase(); } - public KdbDatabaseTest() throws IOException { - super(); + @Override + public void newDatabase() { + kdbDatabase = createDatabase(); + } + + @Override + public Database getDatabase() { + return kdbDatabase; } @Test public void supportedFunctionalityTest(){ - assertFalse(database.supportsBinaryProperties()); - assertFalse(database.supportsNonStandardPropertyNames()); - assertFalse(database.supportsRecycleBin()); + assertFalse(kdbDatabase.supportsBinaryProperties()); + assertFalse(kdbDatabase.supportsNonStandardPropertyNames()); + assertFalse(kdbDatabase.supportsRecycleBin()); + assertFalse(kdbDatabase.supportsPropertyValueStrategy()); } } diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java index 14c063f1..75c1fc58 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java @@ -33,7 +33,6 @@ public class KdbSerializerTest { static PrintStream printStream = getTestPrintStream(); - @Test public void testCreateKdbDatabase() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); diff --git a/test/src/test/resources/kdb.key b/kdb/src/test/resources/kdb.key similarity index 100% rename from test/src/test/resources/kdb.key rename to kdb/src/test/resources/kdb.key diff --git a/test/src/test/resources/kdbwithkey.kdb b/kdb/src/test/resources/kdbwithkey.kdb similarity index 100% rename from test/src/test/resources/kdbwithkey.kdb rename to kdb/src/test/resources/kdbwithkey.kdb diff --git a/kdb/src/test/resources/test123.kdb b/kdb/src/test/resources/test123.kdb new file mode 100755 index 0000000000000000000000000000000000000000..0cfad3b431ef99c496b70b918f7fb953881a9c8b GIT binary patch literal 3148 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 org.linguafranca.pwdb - test + database ${project.version} test-jar test diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index 926e60a9..d322a498 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -194,10 +194,10 @@ public void setDescription(String s) { setDirty(true); } - @Override +/* @Override public boolean shouldProtect(String s) { return keePassFile.meta.memoryProtection.shouldProtect(s); - } + }*/ public List getBinaries() { if (keePassFile.meta.binaries == null) { diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java similarity index 94% rename from example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java rename to kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java index 758e11a2..02cc9757 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/Util.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java @@ -15,13 +15,13 @@ * */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.jackson.util; import com.google.common.io.CharStreams; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; @@ -59,7 +59,7 @@ public static String streamToString(InputStream inputStream) throws IOException */ public static void listDatabase(String resourceName, Credentials creds, OutputStream outputStream) throws IOException { KdbxDatabase database = KdbxDatabase.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); - database.save(new StreamFormat.None(), new KdbxCreds.None(), outputStream); + database.save(new StreamFormat.None(), new KdbxCredentials.None(), outputStream); } /** @@ -67,7 +67,7 @@ public static void listDatabase(String resourceName, Credentials creds, OutputSt */ public static void listDatabase(DatabaseLoader loader, String resourceName, Credentials creds, OutputStream outputStream) throws IOException { Database database = loader.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); - database.save(new StreamFormat.None(), new KdbxCreds.None(), outputStream); + database.save(new StreamFormat.None(), new KdbxCredentials.None(), outputStream); outputStream.flush(); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java deleted file mode 100644 index 7ee7e19c..00000000 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV3Test.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; - -public class JacksonBinaryPropertyV3Test extends BinaryPropertyChecks { - - - public JacksonBinaryPropertyV3Test() { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); - try { - database = KdbxDatabase.load(new KdbxCreds("123".getBytes()),inputStream); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return KdbxDatabase.load(credentials, inputStream); - } - - @Override - public Database newDatabase() { - Database result = null; - try { - result = new KdbxDatabase(); - } catch(Exception e) { - throw new RuntimeException(e); - } - return result; - - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - -} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java deleted file mode 100644 index 7d682a63..00000000 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonBinaryPropertyV4Test.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.linguafranca.pwdb; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -import org.linguafranca.pwdb.checks.BinaryPropertyChecks; -import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; - -public class JacksonBinaryPropertyV4Test extends BinaryPropertyChecks{ - - - public JacksonBinaryPropertyV4Test() throws Exception{ - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-Argon2-Attachment.kdbx"); - database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); - } - - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - try { - return KdbxDatabase.load(credentials, inputStream); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - @Override - public Database newDatabase() { - Database result = null; - try { - result = new KdbxDatabase(); - } catch (Exception e) { - throw new RuntimeException(e); - } - return result; - - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - -} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java deleted file mode 100644 index cdcbcd3f..00000000 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonPropertyValueTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.linguafranca.pwdb; - -import org.linguafranca.pwdb.checks.PropertyValueChecks; -import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class JacksonPropertyValueTest extends PropertyValueChecks { - - public JacksonPropertyValueTest() throws IOException { - super(true); - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - database.save(credentials, outputStream); - } - - @Override - public KdbxDatabase loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { - return KdbxDatabase.load(credentials, inputStream); - } - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - @Override - public KdbxDatabase createDatabase() throws IOException { - return new KdbxDatabase(); - } - -} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java deleted file mode 100644 index b63ad2c7..00000000 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseTest.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.linguafranca.pwdb; - -import java.io.IOException; - -import org.linguafranca.pwdb.checks.BasicDatabaseChecks; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; - -public class KdbxDatabaseTest extends BasicDatabaseChecks { - - public KdbxDatabaseTest() throws IOException { - - } - - - @Override - public Database createDatabase() throws IOException { - return new KdbxDatabase(); - } - -} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java similarity index 53% rename from kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java index fa40a413..8d8f650f 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoaderTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java @@ -14,22 +14,40 @@ * limitations under the License. * */ -package org.linguafranca.pwdb; -import java.io.InputStream; +package org.linguafranca.pwdb.kdbx.database; -import org.linguafranca.pwdb.checks.DatabaseLoaderChecks; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import org.linguafranca.pwdb.test.Test123Test; + +import java.io.IOException; +import java.io.InputStream; + +/** + * @author jo + */ +public class Kdbx123Test implements Test123Test { -public class KdbxDatabaseLoaderTest extends DatabaseLoaderChecks{ - - - public KdbxDatabaseLoaderTest() throws Exception{ + KdbxDatabase kdbxDatabase; + + Kdbx123Test() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); // file has password credentials - Credentials credentials = new KdbxCreds("123".getBytes()); - super.database = KdbxDatabase.load(credentials, inputStream); + Credentials credentials = new KdbxCredentials("123".getBytes()); + // open database. + kdbxDatabase = KdbxDatabase.load(credentials, inputStream); + } + + @Override + public Database getDatabase() { + return kdbxDatabase; } + @Override + public boolean getSkipDateCheck() { + return false; + } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java new file mode 100644 index 00000000..f7f39c4e --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.linguafranca.pwdb.kdbx.database; + +import org.junit.jupiter.api.BeforeAll; +import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import org.linguafranca.pwdb.test.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class KdbxBinariesV3Test implements + BinaryPropertiesTest { + + static String OUTPUT_DIRECTORY_PATH = "testOutput"; + public String databaseName = "Attachment.kdbx"; + + @BeforeAll + static void ppt2BeforeAll() throws Exception { + Files.createDirectories(Paths.get(OUTPUT_DIRECTORY_PATH)); + } + + Database database; + { + newDatabase(); + } + /** + * Create a new database + */ + @Override + public Database createDatabase() { + return new KdbxDatabase(); + } + + /** + * Create a new database for default use in tests + */ + @Override + public void newDatabase() { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream(databaseName); + try { + database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()),inputStream); + } catch (Exception e) { + throw new IllegalStateException(e); + } + } + + /** + * Get the current default database + */ + @Override + public Database getDatabase() { + return database; + } + + @Override + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException{ + database.save(credentials, outputStream); + } + + @Override + public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ + return KdbxDatabase.load(credentials, inputStream); + } + + @Override + public Credentials getCredentials(byte[] credentials){ + return new KdbxCredentials(credentials); + } +} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV4Test.java similarity index 60% rename from kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV4Test.java index bd40b634..0cc5c347 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonRecycleBinTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV4Test.java @@ -14,19 +14,10 @@ * limitations under the License. * */ -package org.linguafranca.pwdb; -import org.linguafranca.pwdb.checks.RecycleBinChecks; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +package org.linguafranca.pwdb.kdbx.database; -public class JacksonRecycleBinTest extends RecycleBinChecks { +public class KdbxBinariesV4Test extends KdbxBinariesV3Test { + String databaseName = "V4-ChaCha20-Argon2-Attachment.kdbx"; - public JacksonRecycleBinTest() { - try { - database = new KdbxDatabase(); - } catch(Exception e) { - e.printStackTrace(); - } - - } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java similarity index 82% rename from kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java index f00aeb6f..e6973d60 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/KdbxDatabaseLoadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java @@ -14,13 +14,15 @@ * limitations under the License. * */ -package org.linguafranca.pwdb; +package org.linguafranca.pwdb.kdbx.database; import java.io.InputStream; import java.io.PrintStream; import org.junit.Test; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.Visitor; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -39,14 +41,14 @@ public void loadXml() throws Exception { @Test public void loadKdbx() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - KdbxDatabase database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); + KdbxDatabase database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); database.visit(new Visitor.Print(printStream)); } @Test public void loadKdbxV4() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); - KdbxDatabase database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); + KdbxDatabase database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); database.visit(new Visitor.Print(printStream)); // test what happens to dates in V4 database.visit(new Visitor.Default(){ @@ -66,7 +68,7 @@ public void emptyDb() throws Exception { @Test public void dbWithDeleted() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("testDeleted.kdbx"); - KdbxDatabase database = KdbxDatabase.load(new KdbxCreds("123".getBytes()), inputStream); + KdbxDatabase database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); database.visit(new Visitor.Print(printStream)); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java new file mode 100644 index 00000000..94cdad4b --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.linguafranca.pwdb.kdbx.database; + +import org.junit.jupiter.api.BeforeAll; +import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import org.linguafranca.pwdb.test.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; + +public class KdbxDatabaseTest implements + TrivialDatabaseTest, + GroupsAndEntriesTest, + RecycleBinTest, + ProtectedPropertyTest, + ProtectedPropertyTest2 { + + static String OUTPUT_DIRECTORY_PATH = "testOutput"; + @BeforeAll + static void ppt2BeforeAll() throws Exception { + Files.createDirectories(Paths.get(OUTPUT_DIRECTORY_PATH)); + } + + Database database; + + /** + * Create a new database + */ + @Override + public Database createDatabase() { + return new KdbxDatabase(); + } + + /** + * Create a new database for default use in tests + */ + @Override + public void newDatabase() { + database = createDatabase(); + } + + /** + * Get the current default database + */ + @Override + public Database getDatabase() { + return database; + } + + @Override + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException{ + database.save(credentials, outputStream); + } + + @Override + public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ + return KdbxDatabase.load(credentials, inputStream); + } + + @Override + public Credentials getCredentials(byte[] credentials){ + return new KdbxCredentials(credentials); + } +} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java similarity index 55% rename from kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java index 4edafcbb..366dd7c8 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/JacksonSaveAndReloadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java @@ -14,47 +14,72 @@ * limitations under the License. * */ -package org.linguafranca.pwdb; +package org.linguafranca.pwdb.kdbx.database; + +import org.junit.jupiter.api.BeforeAll; +import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.StreamFormat; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import org.linguafranca.pwdb.test.*; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; -import org.linguafranca.pwdb.checks.SaveAndReloadChecks; -import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.format.KdbxHeader; +public class KdbxSaveAndReloadTest implements + KdbxFileSaveAndReloadTest { -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; + static String OUTPUT_DIRECTORY_PATH = "testOutput"; + @BeforeAll + static void ppt2BeforeAll() throws Exception { + Files.createDirectories(Paths.get(OUTPUT_DIRECTORY_PATH)); + } + Database database; -public class JacksonSaveAndReloadTest extends SaveAndReloadChecks{ - + /** + * Create a new database + */ @Override - public KdbxDatabase getDatabase() { - try { - return new KdbxDatabase(); - } catch(Exception e) { - return null; - } - + public Database createDatabase() { + return new KdbxDatabase(); } + /** + * Create a new database for default use in tests + */ @Override - public KdbxDatabase getDatabase(String name, Credentials credentials) throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(name); - return KdbxDatabase.load(credentials, inputStream); + public void newDatabase() { + database = createDatabase(); } + /** + * Get the current default database + */ @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { + public Database getDatabase() { + return database; + } + + @Override + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException{ database.save(credentials, outputStream); } @Override - public KdbxDatabase loadDatabase(Credentials credentials, InputStream inputStream) throws IOException { + public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ return KdbxDatabase.load(credentials, inputStream); } + @Override + public Credentials getCredentials(byte[] credentials){ + return new KdbxCredentials(credentials); + } @Override public boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2) { @@ -65,11 +90,4 @@ public boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2) { h1.getKeyDerivationFunction().equals(h2.getKeyDerivationFunction()) && h1.getCipherAlgorithm().equals(h2.getCipherAlgorithm())); } - - - @Override - public Credentials getCreds(byte[] creds) { - return new KdbxCreds(creds); - } - } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java similarity index 78% rename from example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java index 060bd566..b0084bd7 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/UtilTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java @@ -15,36 +15,37 @@ * */ -package org.linguafranca.pwdb.kdbx; +package org.linguafranca.pwdb.kdbx.database.util; import org.junit.Test; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; -import static org.linguafranca.pwdb.kdbx.Util.*; +import static org.linguafranca.pwdb.kdbx.jackson.util.Util.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; public class UtilTest { OutputStream outputStream = getTestPrintStream(); + @Test public void listDatabaseTest() throws IOException { - listDatabase("V3-CustomIcon.kdbx", new KdbxCreds("123".getBytes()), outputStream); + listDatabase("V3-CustomIcon.kdbx", new KdbxCredentials("123".getBytes()), outputStream); } @Test public void listXmlTest() throws IOException { PrintWriter writer = new PrintWriter(outputStream); - listXml("Database-4.1-123.kdbx", new KdbxCreds("123".getBytes()), writer); + listXml("Database-4.1-123.kdbx", new KdbxCredentials("123".getBytes()), writer); writer.flush(); } @Test public void listXmlTest2() throws IOException { PrintWriter writer = new PrintWriter(outputStream); - listXml("issue-70/test2.kdbx", new KdbxCreds("KeePassJava2".getBytes()), writer); + listXml("issue-70/test2.kdbx", new KdbxCredentials("KeePassJava2".getBytes()), writer); writer.flush(); } /** @@ -65,7 +66,7 @@ public void listKdbxHeaderParams () throws IOException { public void listHeaderPropertiesAndXml() throws IOException { PrintWriter writer = new PrintWriter(outputStream); listKdbxHeaderProperties("V4-AES-Argon2-CustomIcon.kdbx", writer); - listXml("V4-AES-Argon2-CustomIcon.kdbx", new KdbxCreds("123".getBytes()), writer); + listXml("V4-AES-Argon2-CustomIcon.kdbx", new KdbxCredentials("123".getBytes()), writer); writer.flush(); } } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java similarity index 89% rename from example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java index ee20352e..40c86818 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue27Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java @@ -15,14 +15,14 @@ * */ -package org.linguafranca.pwdb.kdbx.validation; +package org.linguafranca.pwdb.kdbx.database.validation; import com.google.common.io.CharStreams; import org.junit.Ignore; import org.junit.Test; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.Helpers; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; @@ -45,7 +45,7 @@ public class Issue27Test { @Test public void testIssue27() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); - KdbxCreds creds = new KdbxCreds("passwordless".getBytes()); + KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); KdbxDatabase db = KdbxDatabase.load(creds, is); List entries = db.findEntries("testtitle"); @@ -61,7 +61,7 @@ public void testIssue27() throws IOException { @Test public void testV4Date() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("V4-AES-AES.kdbx"); - KdbxCreds creds = new KdbxCreds("123".getBytes()); + KdbxCredentials creds = new KdbxCredentials("123".getBytes()); KdbxDatabase db = KdbxDatabase.load(creds, is); List entries = db.findEntries("Sample Entry #2 - Copy"); @@ -75,7 +75,7 @@ public void testV4Date() throws IOException { public void testIssue27XML() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); - KdbxCreds creds = new KdbxCreds("passwordless".getBytes()); + KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); InputStream plainText = KdbxSerializer.createUnencryptedInputStream(creds,new KdbxHeader(), is); printStream.println(CharStreams.toString(new InputStreamReader(plainText, StandardCharsets.UTF_8))); } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java similarity index 88% rename from example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java index 0a692bf4..1aeaf733 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue33Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java @@ -15,7 +15,7 @@ * */ -package org.linguafranca.pwdb.kdbx.validation; +package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.Before; @@ -24,8 +24,8 @@ import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.StreamFormat; import org.linguafranca.pwdb.format.Helpers; -import org.linguafranca.pwdb.format.KdbxCreds; -import org.linguafranca.pwdb.kdbx.Util; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.kdbx.jackson.util.Util; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.*; @@ -40,7 +40,7 @@ public class Issue33Test { public static final String TEST_RESOURCE = "V4-AES-Argon2-CustomIcon.kdbx"; public static final String TEST_OUTPUT_DIR = "testOutput"; public static final File TEST_XML_FILE = Paths.get(TEST_OUTPUT_DIR,"Issue33Source.xml").toFile(); - public static final KdbxCreds CREDENTIALS = new KdbxCreds("123".getBytes()); + public static final KdbxCredentials CREDENTIALS = new KdbxCredentials("123".getBytes()); InputStream inputStream; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java similarity index 83% rename from example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java index 63c85322..f0dd8cbd 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue38Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java @@ -15,11 +15,11 @@ * */ -package org.linguafranca.pwdb.kdbx.validation; +package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.Test; import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.IOException; @@ -37,10 +37,10 @@ public class Issue38Test { static PrintStream printStream = getTestPrintStream(); @Test public void testV2Keyfile() throws IOException { - InputStream databaseStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database/Database.kdbx"); - InputStream keyStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database/Database.keyx"); + InputStream databaseStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database.kdbx"); + InputStream keyStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database.keyx"); assert keyStream != null; - KdbxCreds creds = new KdbxCreds("MyPassword".getBytes(), keyStream); + KdbxCredentials creds = new KdbxCredentials("MyPassword".getBytes(), keyStream); assert databaseStream != null; KdbxDatabase database = KdbxDatabase.load(creds, databaseStream); List entries = database.findEntries("Sample Entry"); diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java similarity index 83% rename from example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java index dfacfb90..d9702b64 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/validation/Issue70Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java @@ -15,10 +15,10 @@ * */ -package org.linguafranca.pwdb.kdbx.validation; +package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.Test; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.IOException; @@ -30,11 +30,11 @@ public class Issue70Test { public static final String TEST_RESOURCE1 = "issue-70/test2.kdbx"; - public static final KdbxCreds CREDENTIALS1 = new KdbxCreds("KeePassJava2".getBytes()); + public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials("KeePassJava2".getBytes()); public static final String TEST_RESOURCE2 = "Database-4.1-123.kdbx"; - public static final KdbxCreds CREDENTIALS2 = new KdbxCreds("123".getBytes()); + public static final KdbxCredentials CREDENTIALS2 = new KdbxCredentials("123".getBytes()); public static final String TEST_RESOURCE3 = "issue-70/test-hugoo10.kdbx"; - public static final KdbxCreds CREDENTIALS3 = new KdbxCreds("test".getBytes()); + public static final KdbxCredentials CREDENTIALS3 = new KdbxCredentials("test".getBytes()); /* custom data missing from meta */ diff --git a/test/src/test/resources/Attachment.kdbx b/kdbx-database/src/test/resources/Attachment.kdbx similarity index 100% rename from test/src/test/resources/Attachment.kdbx rename to kdbx-database/src/test/resources/Attachment.kdbx diff --git a/test/src/test/resources/Attachment.xml b/kdbx-database/src/test/resources/Attachment.xml similarity index 100% rename from test/src/test/resources/Attachment.xml rename to kdbx-database/src/test/resources/Attachment.xml diff --git a/test/src/test/resources/Database-4.1-123.kdbx b/kdbx-database/src/test/resources/Database-4.1-123.kdbx similarity index 100% rename from test/src/test/resources/Database-4.1-123.kdbx rename to kdbx-database/src/test/resources/Database-4.1-123.kdbx diff --git a/test/src/test/resources/issue-38/Database/Database.kdbx b/kdbx-database/src/test/resources/Database/Database.kdbx similarity index 100% rename from test/src/test/resources/issue-38/Database/Database.kdbx rename to kdbx-database/src/test/resources/Database/Database.kdbx diff --git a/test/src/test/resources/issue-38/Database/Database.keyx b/kdbx-database/src/test/resources/Database/Database.keyx similarity index 100% rename from test/src/test/resources/issue-38/Database/Database.keyx rename to kdbx-database/src/test/resources/Database/Database.keyx diff --git a/test/src/test/resources/issue-38/Database/README.md b/kdbx-database/src/test/resources/Database/README.md similarity index 100% rename from test/src/test/resources/issue-38/Database/README.md rename to kdbx-database/src/test/resources/Database/README.md diff --git a/test/src/test/resources/ExampleDatabase.kdbx b/kdbx-database/src/test/resources/ExampleDatabase.kdbx similarity index 100% rename from test/src/test/resources/ExampleDatabase.kdbx rename to kdbx-database/src/test/resources/ExampleDatabase.kdbx diff --git a/test/src/test/resources/ExampleDatabase.xml b/kdbx-database/src/test/resources/ExampleDatabase.xml similarity index 100% rename from test/src/test/resources/ExampleDatabase.xml rename to kdbx-database/src/test/resources/ExampleDatabase.xml diff --git a/test/src/test/resources/KeyFileDatabase.kdbx b/kdbx-database/src/test/resources/KeyFileDatabase.kdbx similarity index 100% rename from test/src/test/resources/KeyFileDatabase.kdbx rename to kdbx-database/src/test/resources/KeyFileDatabase.kdbx diff --git a/test/src/test/resources/V3-CustomIcon.kdbx b/kdbx-database/src/test/resources/V3-CustomIcon.kdbx similarity index 100% rename from test/src/test/resources/V3-CustomIcon.kdbx rename to kdbx-database/src/test/resources/V3-CustomIcon.kdbx diff --git a/kdbx-database/src/test/resources/V4-AES-AES.kdbx b/kdbx-database/src/test/resources/V4-AES-AES.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..da60da3b86e0c2440fbf991d81a2197eaa9b116e GIT binary patch literal 2055 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 literal 0 HcmV?d00001 diff --git a/test/src/test/resources/V4-AES-Argon2-CustomIcon.kdbx b/kdbx-database/src/test/resources/V4-AES-Argon2-CustomIcon.kdbx similarity index 100% rename from test/src/test/resources/V4-AES-Argon2-CustomIcon.kdbx rename to kdbx-database/src/test/resources/V4-AES-Argon2-CustomIcon.kdbx diff --git a/test/src/test/resources/V4-AES-Argon2.kdbx b/kdbx-database/src/test/resources/V4-AES-Argon2.kdbx similarity index 100% rename from test/src/test/resources/V4-AES-Argon2.kdbx rename to kdbx-database/src/test/resources/V4-AES-Argon2.kdbx diff --git a/test/src/test/resources/V4-ChaCha20-AES.kdbx b/kdbx-database/src/test/resources/V4-ChaCha20-AES.kdbx similarity index 100% rename from test/src/test/resources/V4-ChaCha20-AES.kdbx rename to kdbx-database/src/test/resources/V4-ChaCha20-AES.kdbx diff --git a/kdbx-database/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx b/kdbx-database/src/test/resources/V4-ChaCha20-Argon2-Attachment.kdbx new file mode 100755 index 0000000000000000000000000000000000000000..59b73e50384873b5a535397de194d41c067b326d GIT binary patch literal 9842 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 Ivq8e}=~+^NzW@LL literal 0 HcmV?d00001 diff --git a/kdbx-database/src/test/resources/issue-38/Database.keyx b/kdbx-database/src/test/resources/issue-38/Database.keyx new file mode 100644 index 00000000..fe8be0de --- /dev/null +++ b/kdbx-database/src/test/resources/issue-38/Database.keyx @@ -0,0 +1,12 @@ + + + + 2.0 + + + + A700DD37 8EBAABE2 1936E1F9 9FED8DEE + D0F71664 B25B0DD2 961FFDA6 9E194E75 + + + \ No newline at end of file diff --git a/kdbx-database/src/test/resources/issue-38/README.md b/kdbx-database/src/test/resources/issue-38/README.md new file mode 100644 index 00000000..9098fa49 --- /dev/null +++ b/kdbx-database/src/test/resources/issue-38/README.md @@ -0,0 +1 @@ +This was rasied as Issue 38 - Keyfile is version 2, which was not supported. \ No newline at end of file diff --git a/test/src/test/resources/issue-70/test-hugoo10.kdbx b/kdbx-database/src/test/resources/issue-70/test-hugoo10.kdbx similarity index 100% rename from test/src/test/resources/issue-70/test-hugoo10.kdbx rename to kdbx-database/src/test/resources/issue-70/test-hugoo10.kdbx diff --git a/test/src/test/resources/issue-70/test1.kdbx b/kdbx-database/src/test/resources/issue-70/test1.kdbx similarity index 100% rename from test/src/test/resources/issue-70/test1.kdbx rename to kdbx-database/src/test/resources/issue-70/test1.kdbx diff --git a/test/src/test/resources/issue-70/test2.kdbx b/kdbx-database/src/test/resources/issue-70/test2.kdbx similarity index 100% rename from test/src/test/resources/issue-70/test2.kdbx rename to kdbx-database/src/test/resources/issue-70/test2.kdbx diff --git a/test/src/test/resources/kdbxSample.xml b/kdbx-database/src/test/resources/kdbxSample.xml similarity index 100% rename from test/src/test/resources/kdbxSample.xml rename to kdbx-database/src/test/resources/kdbxSample.xml diff --git a/test/src/test/resources/kdbx_hash_test_wrong_hash.keyx b/kdbx-database/src/test/resources/kdbx_hash_test_wrong_hash.keyx similarity index 100% rename from test/src/test/resources/kdbx_hash_test_wrong_hash.keyx rename to kdbx-database/src/test/resources/kdbx_hash_test_wrong_hash.keyx diff --git a/test/src/test/resources/letter J.jpeg b/kdbx-database/src/test/resources/letter J.jpeg similarity index 100% rename from test/src/test/resources/letter J.jpeg rename to kdbx-database/src/test/resources/letter J.jpeg diff --git a/test/src/test/resources/letter L.jpeg b/kdbx-database/src/test/resources/letter L.jpeg similarity index 100% rename from test/src/test/resources/letter L.jpeg rename to kdbx-database/src/test/resources/letter L.jpeg diff --git a/kdbx-database/src/test/resources/test1.kdbx b/kdbx-database/src/test/resources/test1.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..afda971d9b0b7900845cb4ad8556deb3da51c242 GIT binary patch literal 2362 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 MGxK)6;b$G>0%pC7UH||9 literal 0 HcmV?d00001 diff --git a/test/src/test/resources/testDeleted.kdbx b/kdbx-database/src/test/resources/testDeleted.kdbx similarity index 100% rename from test/src/test/resources/testDeleted.kdbx rename to kdbx-database/src/test/resources/testDeleted.kdbx diff --git a/test/src/test/resources/xml/V4-AES-AES.xml b/kdbx-database/src/test/resources/xml/V4-AES-AES.xml similarity index 100% rename from test/src/test/resources/xml/V4-AES-AES.xml rename to kdbx-database/src/test/resources/xml/V4-AES-AES.xml diff --git a/test/src/test/resources/xml/V4-AES-Argon2.xml b/kdbx-database/src/test/resources/xml/V4-AES-Argon2.xml similarity index 100% rename from test/src/test/resources/xml/V4-AES-Argon2.xml rename to kdbx-database/src/test/resources/xml/V4-AES-Argon2.xml diff --git a/test/src/test/resources/xml/V4-ChaCha20-AES.xml b/kdbx-database/src/test/resources/xml/V4-ChaCha20-AES.xml similarity index 100% rename from test/src/test/resources/xml/V4-ChaCha20-AES.xml rename to kdbx-database/src/test/resources/xml/V4-ChaCha20-AES.xml diff --git a/test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml b/kdbx-database/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml similarity index 100% rename from test/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml rename to kdbx-database/src/test/resources/xml/V4-ChaCha20-Argon2-Attachment.xml diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index ac9d8be6..32bcbeb4 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -45,7 +45,7 @@ org.linguafranca.pwdb - test + database ${project.version} test-jar test diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java index 7a30dc68..d0bc5c7b 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java @@ -21,7 +21,7 @@ import com.google.common.io.LittleEndianDataInputStream; import org.junit.Ignore; import org.junit.Test; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; import org.linguafranca.pwdb.hashedblock.HmacBlockInputStream; @@ -46,7 +46,7 @@ public void getHmacStream() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); printStream.println("Version " + header.getVersion()); - KdbxCreds creds = new KdbxCreds("123".getBytes()); + KdbxCredentials creds = new KdbxCredentials("123".getBytes()); assert inputStream != null; //noinspection UnstableApiUsage KdbxSerializer.readOuterHeaderVerification(header, creds, new LittleEndianDataInputStream(inputStream)); diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java index e5fad1a8..0db4fc9d 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java @@ -20,7 +20,7 @@ import com.google.common.io.CharStreams; import org.junit.Test; import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxKeyFile; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -62,7 +62,7 @@ public void testLoad() throws Exception { @Test public void testEmptyPasswordCreds() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPassword.kdbx"); - Credentials credentials = new KdbxCreds(new byte[0]); + Credentials credentials = new KdbxCredentials(new byte[0]); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -75,7 +75,7 @@ public void testEmptyPasswordKeyCreds() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPasswordWithKey.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("EmptyPasswordWithKey.key"); assert inputStreamKeyFile != null; - Credentials credentials = new KdbxCreds(new byte[0], inputStreamKeyFile); + Credentials credentials = new KdbxCredentials(new byte[0], inputStreamKeyFile); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -88,7 +88,7 @@ public void testNoPasswordKeyCreds() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("NoPasswordWithKey.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("NoPasswordWithKey.key"); assert inputStreamKeyFile != null; - Credentials credentials = new KdbxCreds(inputStreamKeyFile); + Credentials credentials = new KdbxCredentials(inputStreamKeyFile); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -99,7 +99,7 @@ public void testNoPasswordKeyCreds() throws Exception { @Test public void testEmptyPassword() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPassword.kdbx"); - Credentials credentials = new KdbxCreds(new byte[0]); + Credentials credentials = new KdbxCredentials(new byte[0]); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -111,7 +111,7 @@ public void testEmptyPassword() throws Exception { public void testSignedKeyFile() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.keyx"); - Credentials credentials = new KdbxCreds("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -123,7 +123,7 @@ public void testSignedKeyFile() throws Exception { public void testSignatureFails() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test_wrong_hash.keyx"); - Credentials credentials = new KdbxCreds("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -135,7 +135,7 @@ public void testSignatureFails() throws Exception { public void testKeyFileRandom() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdb_with_random_file.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("random_file"); - Credentials credentials = new KdbxCreds("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -147,7 +147,7 @@ public void testKeyFileRandom() throws Exception { public void testKeyFileHex64() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_keyfile64.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile64"); - Credentials credentials = new KdbxCreds("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -159,7 +159,7 @@ public void testKeyFileHex64() throws Exception { public void testKeyFile32() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_keyfile32.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile32"); - Credentials credentials = new KdbxCreds("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java index 53998b81..fde4ced3 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java @@ -20,7 +20,7 @@ import com.google.common.io.CharStreams; import org.junit.Test; import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.format.KdbxCreds; +import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -44,7 +44,7 @@ public class KdbxSerializerTest { @Test public void testGetPlainTextInputStream() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); - Credentials credentials = new KdbxCreds("123".getBytes()); + Credentials credentials = new KdbxCredentials("123".getBytes()); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); printStream.println(CharStreams.toString(new InputStreamReader(decryptedInputStream, StandardCharsets.UTF_8))); } @@ -55,7 +55,7 @@ public void testGetPlainTextInputStream() throws Exception { @Test public void testGetPlainTextInputStream2() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-AES.kdbx"); - Credentials credentials = new KdbxCreds("123".getBytes()); + Credentials credentials = new KdbxCredentials("123".getBytes()); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); printStream.println(CharStreams.toString(new InputStreamReader(decryptedInputStream, StandardCharsets.UTF_8))); } @@ -69,7 +69,7 @@ public void testGetPlainTextInputStream2() throws Exception { public void testCypherTextOutputStream() throws Exception { File tempFile = File.createTempFile("test", "test"); OutputStream testStream = new FileOutputStream(tempFile); - Credentials credentials = new KdbxCreds("123".getBytes()); + Credentials credentials = new KdbxCredentials("123".getBytes()); OutputStream outputStream = KdbxSerializer.createEncryptedOutputStream(credentials, new KdbxHeader(), testStream); outputStream.write("Hello World\n".getBytes()); @@ -89,7 +89,7 @@ public void testCypherTextOutputStream() throws Exception { public void testCypherTextOutputStream2() throws Exception { File tempFile = File.createTempFile("test", "test"); OutputStream testStream = new FileOutputStream(tempFile); - Credentials credentials = new KdbxCreds("123".getBytes()); + Credentials credentials = new KdbxCredentials("123".getBytes()); KdbxHeader kdbxHeader = new KdbxHeader(4); OutputStream outputStream = KdbxSerializer.createEncryptedOutputStream(credentials, kdbxHeader, testStream); diff --git a/kdbx-io/src/test/resources/Attachment.kdbx b/kdbx-io/src/test/resources/Attachment.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..12a1c1bb3cb98527f324535344c84dabee6f1ae1 GIT binary patch literal 10126 zcmV;9Cvn&V*`k_f`%AR}00RI55CAd3^5(yBLr}h01tDtuTK@wC0096100bZa>fr>i zczeC^wOkJtm^e6Vs)m2^K+eRfdMGe#$)Uol1t0(|KhXWRSQkwdgv;rfK?rtn;2Z)2 znI)|)4$-(uPq={w2mnyS00000000LN0L0~~N(x_0^NhO^T6lA(?g$_N!rOi#Se(uO zy0uNwH)WDq5yOMAGO5roNa-ZvK1Cb`2_OJ<@aRfyf}G6Y4{KY(xn=)pGd!XkhRt0q z9^6Q(l{HaBAvX(L6O@Gzu~k5+ z?db05a`(54%z&^Y@=pHoO-xpmt7c7Sb;YIp9&*%DZ(EEaho5I;C|Em zC34&24H%`s71N2-nr2{^NT2Jg=?P~!!S8`=pou`u3%`^1o33It6NaUO-7wCoB`Wcm zj`%ZCIgVWH+MDplAMcOs9dhWyzF2WwGf;yL7G$d7_MauKDBXjWRe>8?LzX#yd*&tF z_%nrp3u8Tirr0RtDmA1rHDIS%?PEDNU3rXJwD_`Zps8yKBE~q+`q58$Mt-AVyzDsR zN4{MoE2~aZ(;tITLg57SZ<|*tlO_@pWcaa9_OyBFQ|AY zd_A1dOtCJg$D1lI*iiX_nRO5)i3}Q3@s(yvgsjmC_J2WW#lYr;R#2G&@mGp0EHT5R zVAX;N)pxl5V}6XrYOkNZFRgF`+EDm=w^D(jG)D-t?M=;)9|m6N1@+3g&36p@^$lsM z@6kpg)ipD7G8h@EWyVLqpGd~8fb#I^*B2tlf=SP)rm-^VJBVSkHhW-ZG1l5E$A-Q1zp#L13#wM-f?g1yNw8Nac zYJ+#KMg`|=^vGOfiioYhh$$3hi1+Ms{u~}f8cc|l*1ucxK)_*wb4tq_Q$*?AN}76P zOVw`4h!`b`CUB+jzX={!bkYPVWO7=b53g(oRequ~Yx;VQ);pGbAaPC#U;l}zVWqH& zuaR4b#}VnXue%RS$!-p)+mmb+WzWjT_(kMF#{8cnzTS0Xhg>a1xr>w*#gqjD6QbzH z$Q3^5gP4a&rM0+Y6=O7fB0@{2pGlnrnv3!R#&OtdGnTgK5u96utV%0D`C$4)8Z6lk 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

P|Dqb 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 literal 0 HcmV?d00001 diff --git a/test/src/test/resources/EmptyPassword.kdbx b/kdbx-io/src/test/resources/EmptyPassword.kdbx similarity index 100% rename from test/src/test/resources/EmptyPassword.kdbx rename to kdbx-io/src/test/resources/EmptyPassword.kdbx diff --git a/test/src/test/resources/EmptyPasswordWithKey.kdbx b/kdbx-io/src/test/resources/EmptyPasswordWithKey.kdbx similarity index 100% rename from test/src/test/resources/EmptyPasswordWithKey.kdbx rename to kdbx-io/src/test/resources/EmptyPasswordWithKey.kdbx diff --git a/test/src/test/resources/EmptyPasswordWithKey.key b/kdbx-io/src/test/resources/EmptyPasswordWithKey.key similarity index 100% rename from test/src/test/resources/EmptyPasswordWithKey.key rename to kdbx-io/src/test/resources/EmptyPasswordWithKey.key diff --git a/test/src/test/resources/KeyFileDatabase.key b/kdbx-io/src/test/resources/KeyFileDatabase.key similarity index 100% rename from test/src/test/resources/KeyFileDatabase.key rename to kdbx-io/src/test/resources/KeyFileDatabase.key diff --git a/test/src/test/resources/NoPasswordWithKey.kdbx b/kdbx-io/src/test/resources/NoPasswordWithKey.kdbx similarity index 100% rename from test/src/test/resources/NoPasswordWithKey.kdbx rename to kdbx-io/src/test/resources/NoPasswordWithKey.kdbx diff --git a/test/src/test/resources/NoPasswordWithKey.key b/kdbx-io/src/test/resources/NoPasswordWithKey.key similarity index 100% rename from test/src/test/resources/NoPasswordWithKey.key rename to kdbx-io/src/test/resources/NoPasswordWithKey.key diff --git a/kdbx-io/src/test/resources/V4-AES-AES.kdbx b/kdbx-io/src/test/resources/V4-AES-AES.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..da60da3b86e0c2440fbf991d81a2197eaa9b116e GIT binary patch literal 2055 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 literal 0 HcmV?d00001 diff --git a/kdbx-io/src/test/resources/V4-AES-Argon2.kdbx b/kdbx-io/src/test/resources/V4-AES-Argon2.kdbx new file mode 100755 index 0000000000000000000000000000000000000000..5d9e5794dcb1e1d3132cf5deb6cd54cdf25ed608 GIT binary patch literal 2005 zcmV;`2P*gj*`k_f`%AR|00aO65C8xGF~RcYzi~rQzE}kzYW!ON0|Wp70096100bZa z004prcj3G+nzvL_0%l$7S7HkaJzDp>#jD-9_8&G09F7Z%000000YU`;001OaRY^n; z0002*V{PAzDMU+=_o<`<;|dG}0RR91Rs;Y5022TJ00jX6002n{000020000000005 z0RR91O$Y!00000G0000000aR5002+~000020000&0RR91Qy>5U0O*ze#$VE-%P5+!EHA~^k|257?o1ONa44GIkk z#enBo2_H4T9FO--c**^~WIxHeD^&}9E}HErvCYoIwUfM7m{CKUFk&4!!m+t$=PFLV ze;Y?qheFMNdY3_!e-t;weNty18rQya;f`;4Tyy}1SBZoMVXzfi_85xjuYs!Uj}(h~ogF~r^S zBGR7V(20&XIlVP7HSU*Nx!E$rH@)NKucX2mD8Bv6Ds;? z|I{U2UlgB0jV~f);l5Ez9ArDSRoy@^+gEn5ds3_VxMXEffFH|gaZbB?D&)+uUl5v3OhH-OgXLu*%3Ta~!>#iR~c)rI`orDOfooNO|DCyAjiGff5Jt zDI2UIWrv8$OGr!4J6osuyVwc%+|wb| zW)P|mQzT!G`zF?TPkvi4s)K(cX29ZqBwPEce1!%SNdaN}(}xAWPQTVLy}l*Kk3V+4 ziev7Jy;bVU?FMBu3}Q9G|0Wjh+9S#8yBw!-k@%R_C&W@5n0}x^G;4rRW9T}Oy3Lcx z)h^@tb@h`EHYfhWvwB^`oHK=yOGo}&A#XX)VLaL zQ-o(T^14%tj>rD?sDZ^7$6P6<9XRI{&O^58&ubArJy5UhjvAJGpbXa~qh}A~M_$Eo zmr~Juq?Op?IOMH7J|Y8tI(|p-jT}Dc?gQXHbtA0U;NJ9gw6#44euBwd%@>cyNkCL} ztu#)nN&GH!c)p$2ZLSN7i;?qouRN4^!V3He2*bK9ys?qvNAgoebBHV`i_zGE5!dmW ze_;RYQ8}c{!$Df^(5OSf>LTKF{Hmx+s;=-*z@Z&totwaomm0rUV%f{XN|RwS7`npK zaqXcd9@rI=$h59?VT?17#7M7Ma(LFX93}RY`f07YTwx!}fay-pXI~C;m>h`9Aony7 ztZ52R;|Ae#xu(I(i4K!i->TOvv!>?Z^&hqSmLlDssO`|EUH_9tjEf6`B;aTuI)U#4 z6!y0GRW7!eG9?#!Sl}_U$?dMoU^?{#tHSK>Z8@k@6uUU!qLYtq8`0hT$|mmg;X^wP z0nlj0GE-z0&B4Gm5QQM=nA&~g=R*4C(>|b$O+tMmc1+>-6gxRrOSt(T0|3U+RiMA` zylei+K$3UzjTLq&FE3+t6}RawN=iZYXP@q3Z(kmgRTItS%Bw-AX2Hqi!oaj_}DWv zjh51LSL`?t$JdZubbKB3kCehn-)%w!X7^jNY0%NirZ#UIsc_1_X literal 0 HcmV?d00001 diff --git a/kdbx-io/src/test/resources/V4-ChaCha20-AES.kdbx b/kdbx-io/src/test/resources/V4-ChaCha20-AES.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..9f33a1f111518badf9757b20a8d8384f5a529d37 GIT binary patch literal 2039 zcmVt<800BY;0000aRaHqu5C8xG$=UOoVv0myzjO@00000 z002S(0000}AOHXWPB-DhX^ZtLzp(3fRR3jv&RYo}q0Y!`fL<~UOq2y`000C4000dN z4GJtua!^+GpZ6R=J|vj{&J&qDt2AM$q=nnQ(abO`Xe~rp~OC#Y820yauHL zv{QWG@vpt)+%%#nXN3Uc*Ef-W=T_d6amfJI^&((*Q(THw%e{q(Y%fkYysV@K003Z% zwlRql=(TzUTgz3BaY-c``2#IrAahJBwDgU^yw;&I4zedPz=886oEOvxv@vU0xqwJG z#GM#{|NdU|iSx+rEaSfYPbprcCkyyXPTLRZ`@$=sYs%AD|Ml_vtERIGBJu(3=d!oT zy)kTQA@fabI=OJ@@XYAr?A0j(Zy4`Fp2D149O$neb;Wm+I$*Q*Q=8N@VY!8#(${t-{jN`3;Pfx$}R%1u+4M^n!7Utq2hq^Ie0$tXVcux zNj;1rJKxfn&IB*xo&O^K^_>Pc&b=E*Y_C4kBwy}ZpMpK_r4yjbxLij#b@bzz73tsY ztHH#Um8Ivs?AZFG;o1$^aEQ)UXm2e4UVFlP>iBK_54O4H{ zXL4gxey4)RdZm@yr?(ejCCycd53uImC-s<6zADT}?^H%#nuc!C3pvL*n&F6Rydc-C z!Hzm_$LkB%{{LF26R)@vr>}2rYUrS1?;jWO_+R3jF)tTtU-Lv5LnL1@cPQ^zX7I$? zM$|V;CWq73+td}7P>l1tjfk8jv=sJ-HBBaVi~t@N;~0H!SnYlayY?oWEX;aPIW?`B z)wGZYu({4NqgVpmAiQRS2rqNu+Rcd3LDo2fylV!7z5QihYT(j7*@Hyau})EvN>Ig6 zLT!PR4c#wvG7~cjK})a@EV(QzfY*`;L>JbL?&qN+hy06lKAxjlJ>-(g!RS@XPq zpcnF1PYK2MF1%Z^_KRL8Qs{9)2+p4^E5c_OIN{GsqRDiIaUcMHjhW5Y17lFQ%Ri`p zqU!pttKrl07}y?HMR9$5WM?+G2^Yu8gn?^0b`j1whxe4&oISx5Lx}?N&*4MOlwB2# zJ32CRSBVew__4`R$DYfD^| z5SLHruLmy|lIz`8JbXkh2=*P(;QPcw2J!6-cg$xy4e6p z9-O)kd_^YOWI)PU?&sE^Q2snfR9cnn)KG)NBNpd0!?`ri9s1FwYbBY62A`SP9I<5eGR58}~Tj|y_lb~=NyKCG%_S+;NO_Q!>2FaeCX zC;z9}ytu5T1P{<%+UCoWb~tkGHT}y`AX9=>0fkGdZ|Hk%t_ehVLY!$0Ui9M(5f75;s~YW09qBg)^k}3##LN> z!xFyAv4GGBcM_jJQQ7t*72*ecXesRW2G04#ejWxa$juM{EH3^^k@`aZO*ezZ3Tz49 zkPmLk546q04uo{vM@imm^e4*1cpRo-6hqnygW3f!)vlVvH`3?B^{Yat4QKDvS1*)+ z6xTdh5yysxp7A+zKk}p|QY@~*^69syj%W-M)zgGjx*Q78hUz%5M(=2i>}B?_2XE8&3?8#$wD;C)3CzX&IA#6h+LX>raPPGJDqNyBPFLhIySfkVBCDP Vv83wZp~Ixu^I_!5g5U09m_{Qj0oid%F+! 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.0.0-SNAPSHOT database - test - util kdb kdbx-io kdbx-database diff --git a/test/pom.xml b/test/pom.xml deleted file mode 100644 index b7d513dc..00000000 --- a/test/pom.xml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - KeePassJava2.parent - org.linguafranca.pwdb - 3.0.0-SNAPSHOT - - 4.0.0 - - test - PWDB :: Test - Contains the shared test classes for exercising the functionality of a PWDB database implementation. - - - true - - - - - org.linguafranca.pwdb - database - ${project.version} - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - - \ No newline at end of file diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java deleted file mode 100644 index 6353bfcf..00000000 --- a/test/src/test/java/org/linguafranca/pwdb/checks/BasicDatabaseChecks.java +++ /dev/null @@ -1,282 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.checks;/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import org.junit.Test; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.Icon; - -import java.io.IOException; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import static org.junit.Assert.*; - -/** - * @author Jo - */ -public abstract class BasicDatabaseChecks { - - protected Database database; - - public abstract Database createDatabase() throws IOException; - - public BasicDatabaseChecks() throws IOException { - this.database = createDatabase(); - } - - @Test - public void testEmptyDatabase() { - assertEquals("Root", database.getRootGroup().getName()); - assertEquals(0, database.getRootGroup().getEntries().size()); - assertEquals(0, database.getRootGroup().getGroups().size()); - } - - @Test - public void testAddGroup() { - Group g1 = database.getRootGroup().addGroup(database.newGroup("group1")); - assertEquals(1, database.getRootGroup().getGroups().size()); - assertEquals("group1", g1.getName()); - assertEquals(0, g1.getGroups().size()); - assertEquals(0, g1.getEntries().size()); - assertEquals("root is not the parent of its child", g1.getParent(), database.getRootGroup()); - - Group g2 = database.newGroup(); - assertEquals("", g2.getName()); - assertNotNull(g2.getUuid()); - assertEquals(0, g2.getIcon().getIndex()); - assertEquals(0, g2.getGroups().size()); - assertEquals(0, g2.getEntries().size()); - - // show that the list of groups is a copy - database.getRootGroup().getGroups().clear(); - assertEquals(1, database.getRootGroup().getGroups().size()); - assertEquals(0, g1.getEntries().size()); - } - - @Test - public void testDeleteGroup () { - Group g1 = database.getRootGroup().addGroup(database.newGroup("group1")); - List l1 = database.getRootGroup().findGroups("group1"); - assertEquals(1, l1.size()); - Group g2 = l1.get(0); - assertEquals(g2, g1); - Group g3 = database.getRootGroup().removeGroup(g2); - assertEquals(g3, g1); - assertNull(g1.getParent()); - assertEquals(0, database.getRootGroup().getGroups().size()); - assertEquals(0, database.getRootGroup().findGroups("group1").size()); - } - - @Test - public void testAddRemoveEntry() { - Entry e1 = database.getRootGroup().addEntry(database.newEntry()); - e1.setTitle("entry1"); - List l1 = database.findEntries("entry1"); - assertEquals(1, l1.size()); - - Entry e12 = database.getRootGroup().addEntry(database.newEntry("entry12")); - List l2 = database.findEntries("entry1"); - assertEquals(2, l2.size()); - - // show that the entries are different - assertNotEquals(l2.get(0), l2.get(1)); - - // show that the list is a copy - l2.clear(); - assertEquals(2, database.findEntries("entry1").size()); - - // show that we get an equivalent entry when we remove to when we inserted - Entry e12b = database.getRootGroup().removeEntry(e12); - assertEquals(e12b, e12); - // has been unhooked from parent - assertNull(e12.getParent()); - assertEquals(1, database.findEntries("entry1").size()); - } - - @Test - public void testSetFields () { - Entry e1 = database.newEntry("Entry 1"); - e1.setNotes("this looks a little like Entry 2"); - assertEquals("this looks a little like Entry 2", e1.getNotes()); - e1.setUsername("jake@window.com"); - assertEquals("jake@window.com", e1.getUsername()); - e1.setPassword("supercalifragelisticexpialidocious"); - assertEquals("supercalifragelisticexpialidocious", e1.getPassword()); - e1.setUrl("https://window.com"); - assertEquals("https://window.com", e1.getUrl()); - - - assertTrue(e1.match("2")); - assertTrue(e1.matchTitle("1")); - assertFalse(e1.matchTitle("doggy")); - - Icon ic1 = database.newIcon(27); - e1.setIcon(ic1); - assertEquals(e1.getIcon(), ic1); - - // databases have to support setting of standard properties - e1.setProperty(Entry.STANDARD_PROPERTY_NAME_TITLE, "A title"); - assertEquals("A title", e1.getTitle()); - e1.setProperty(Entry.STANDARD_PROPERTY_NAME_USER_NAME, "username"); - assertEquals("username", e1.getUsername()); - e1.setProperty(Entry.STANDARD_PROPERTY_NAME_NOTES, "notes"); - assertEquals("notes", e1.getNotes()); - e1.setProperty(Entry.STANDARD_PROPERTY_NAME_PASSWORD, "password"); - assertEquals("password", e1.getPassword()); - e1.setProperty(Entry.STANDARD_PROPERTY_NAME_URL, "url"); - assertEquals("url", e1.getUrl()); - - try { - e1.setProperty("silly", "hello"); - assertEquals("hello", e1.getProperty("silly")); - List properties = new ArrayList<>(Entry.STANDARD_PROPERTY_NAMES); - properties.add("silly"); - // remove all properties to show that getProperties returns all the values we want - properties.removeAll(e1.getPropertyNames()); - assertEquals(0, properties.size()); - } catch (UnsupportedOperationException e) { - // databases don't have to support arbitrary properties - assertFalse(database.supportsNonStandardPropertyNames()); - assertArrayEquals(e1.getPropertyNames().toArray(), Entry.STANDARD_PROPERTY_NAMES.toArray()); - } - - e1.setNotes("How much is that doggy in the window?"); - assertEquals("How much is that doggy in the window?", e1.getNotes()); - e1.setTitle("Entry 2"); - assertEquals("Entry 2", e1.getTitle()); - assertEquals("Entry 2", e1.getPath()); - } - - @Test - public void testTimes() { - long beforeSecond = Instant.now().toEpochMilli()/1000; - Entry entry = database.newEntry(); - long afterSecond = Instant.now().toEpochMilli()/1000; - long createdSecond = entry.getCreationTime().getTime()/1000; - - assertTrue(createdSecond >= beforeSecond && createdSecond <= afterSecond); - assertFalse(entry.getExpires()); - assertTrue(entry.getLastAccessTime().getTime()/1000 <= createdSecond); - assertTrue(entry.getLastModificationTime().getTime()/1000 <= createdSecond); - - entry.setExpires(true); - entry.setExpiryTime(new Date(createdSecond*1000)); - - assertTrue(entry.getExpires()); - assertEquals(createdSecond, entry.getExpiryTime().getTime()/1000); - - - } - - @Test - public void checkAddChangeRemoveProperty() { - // only applies to databases that support arbitrary properties - Entry entry = database.newEntry(); - assertEquals(Entry.STANDARD_PROPERTY_NAMES.size(), entry.getPropertyNames().size()); - try { - entry.setProperty("test", "test1"); - } catch (UnsupportedOperationException e) { - if (!database.supportsNonStandardPropertyNames()) { - return; - } - fail("Database must report that it doesn't support non standrad properties"); - } - assertEquals("test1", entry.getProperty("test")); - entry.setProperty("test", "test2"); - assertEquals("test2", entry.getProperty("test")); - assertTrue(entry.removeProperty("test")); - assertFalse(entry.removeProperty("test")); - assertFalse(entry.removeProperty("test-test")); - assertEquals(Entry.STANDARD_PROPERTY_NAMES.size(), entry.getPropertyNames().size()); - try { - entry.removeProperty(Entry.STANDARD_PROPERTY_NAME_USER_NAME); - fail("Should not be able to remove standard property"); - } catch (IllegalArgumentException ignore) { - // ignore as expected - } - } - - - @Test - public void testNewEntry() { - Entry e2 = database.newEntry(); - assertNull(e2.getParent()); - assertEquals("", e2.getPassword()); - assertNotNull(e2.getUuid()); - assertEquals("", e2.getUrl()); - assertEquals("", e2.getNotes()); - assertEquals("", e2.getUsername()); - assertEquals("", e2.getTitle()); - assertNull(e2.getProperty("silly")); - List l = e2.getPropertyNames(); - l.removeAll(Entry.STANDARD_PROPERTY_NAMES); - assertEquals(0, l.size()); - } - - @Test - public void testCopy() throws IOException { - Entry entry1 = database.newEntry(); - entry1.setTitle("Entry"); - entry1.setUsername("Username"); - entry1.setPassword("Password"); - entry1.setUrl("https://dont.follow.me"); - entry1.setNotes("Notes"); - entry1.setIcon(database.newIcon(2)); - - // create a new Database - Database database2 = createDatabase(); - // create a new Entry in new Database - Entry entry2 = database2.newEntry(entry1); - - assertEquals(entry1.getTitle(), entry2.getTitle()); - assertEquals(entry1.getUsername(), entry2.getUsername()); - assertEquals(entry1.getPassword(), entry2.getPassword()); - assertEquals(entry1.getUrl(), entry2.getUrl()); - assertEquals(entry1.getNotes(), entry2.getNotes()); - assertEquals(entry1.getIcon(), entry2.getIcon()); - assertNotEquals(entry1.getUuid(), entry2.getUuid()); - - Group group1 = database.newGroup(); - group1.setName("Group"); - group1.setIcon(database.newIcon(3)); - - Group group2 = database2.newGroup(group1); - assertEquals(group1.getName(), group2.getName()); - assertEquals(group1.getIcon(), group2.getIcon()); - assertNotEquals(group1.getUuid(), group2.getUuid()); - } -} diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java deleted file mode 100644 index 9e4b9bf0..00000000 --- a/test/src/test/java/org/linguafranca/pwdb/checks/BinaryPropertyChecks.java +++ /dev/null @@ -1,207 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.checks;/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import com.google.common.io.ByteStreams; -import org.junit.Test; -import org.linguafranca.pwdb.*; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Path; - -import static org.junit.Assert.*; - -/** - * Tests to verify that binary properties work correctly. Resources associated with this module contain files - * named "Attachment*.kdbx" which contain two entries: - * - *

- *
"Test Attachment"
- *
Contains an attachment "Letter J" which is also present in the resources directory
- *
"Test 2 Attachment"
- *
Contains an attachment "Letter L" which is also present in the resources directory
- *
- * - * When used as a test suite for a concrete implementation, subclass and name the class *Test etc. to conform - * with Junit rules - *

- * Subclasses should test both V3 KDBX files (Attachment.kdbx) and V4 (V4-ChaCha20-Argon2-Attachment.kdbx) since - * attachments are handled differently in the two versions. - * - * @author jo - */ -public abstract class BinaryPropertyChecks { - - public Database database; - - @SuppressWarnings("unused") - public abstract void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; - public abstract Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; - public abstract Database newDatabase(); - public abstract Credentials getCreds(byte[] creds); - - /** - * Retrieve and verify attachment "letter J" - */ - @Test - public void getBinaryProperty() throws Exception { - Entry entry = database.findEntries("Test attachment").get(0); - byte [] letterJ = entry.getBinaryProperty("letter J.jpeg"); - InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); - assert testFile != null; - byte [] original = ByteStreams.toByteArray(testFile); - assertArrayEquals(original, letterJ); - } - - /** - * Retrieve and verify attachment "letter L" - */ - @Test - public void getAnotherBinaryProperty() throws Exception { - Entry entry = database.findEntries("Test 2 attachment").get(0); - byte [] letterL = entry.getBinaryProperty("letter L.jpeg"); - InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); - assert testFile != null; - byte [] original = ByteStreams.toByteArray(testFile); - assertArrayEquals(original, letterL); - } - - /** - * Add the Letter L to the entry containing letter J - */ - @Test - public void setBinaryProperty() throws Exception { - InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); - assert testFile != null; - byte [] original = ByteStreams.toByteArray(testFile); - Entry entry = database.findEntries("Test attachment").get(0); - entry.setBinaryProperty("letter L.jpeg", original); - byte [] letterL = entry.getBinaryProperty("letter L.jpeg"); - assertArrayEquals(original, letterL); - assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); - assertTrue(database.isDirty()); - } - - /** - * Verify that the entries have the right attachments - */ - @Test - public void getBinaryPropertyNames() { - Entry entry = database.findEntries("Test attachment").get(0); - assertArrayEquals(new String[] {"letter J.jpeg"}, entry.getBinaryPropertyNames().toArray()); - - entry = database.findEntries("Test 2 attachment").get(0); - assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); - } - - /** - * Verify that the database correctly reports that its supports binary attachments - override for - * databases that don't - */ - @Test - public void checkSupported(){ - assertTrue(database.supportsBinaryProperties()); - } - - /** - * Verify that the binary properties can be added and removed correctly - */ - @Test - public void checkAddChangeRemoveBinaryProperty() { - byte[] test = new byte[] {0, 1, 2 ,3}; - byte[] test2 = new byte[] {3, 2, 1, 0}; - Entry entry = database.findEntries("Test attachment").get(0); - assertEquals(1, entry.getBinaryPropertyNames().size()); - entry.setBinaryProperty("test", test); - assertArrayEquals(test, entry.getBinaryProperty("test")); - entry.setBinaryProperty("test", test2); - assertArrayEquals(test2, entry.getBinaryProperty("test")); - // true that property was removed - assertTrue(entry.removeBinaryProperty("test")); - // false that same property was removed - assertFalse(entry.removeBinaryProperty("test")); - // false that non-existent was removed - assertFalse(entry.removeBinaryProperty("test-test")); - // same number of properties as we started with - assertEquals(1, entry.getBinaryPropertyNames().size()); - } - - /** - * Checks that a database with binary properties saves and reloads correctly - */ - @Test - public void saveAndReloadCheck() throws IOException { - Path file = Files.createTempFile("keepass", "tmp"); - saveDatabase(database, getCreds("123".getBytes()), Files.newOutputStream(file)); - - Database db = loadDatabase(getCreds("123".getBytes()),Files.newInputStream(file)); - Entry newEntry = db.findEntries("Test attachment").get(0); - assertArrayEquals(new String[] {"letter J.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); - Entry oldEntry = database.findEntries("Test attachment").get(0); - assertArrayEquals(oldEntry.getBinaryProperty("letter J.jpeg"), newEntry.getBinaryProperty("letter J.jpeg")); - - newEntry = db.findEntries("Test 2 attachment").get(0); - assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); - oldEntry = database.findEntries("Test 2 attachment").get(0); - assertArrayEquals(oldEntry.getBinaryProperty("letter J.jpeg"), newEntry.getBinaryProperty("letter J.jpeg")); - assertArrayEquals(oldEntry.getBinaryProperty("letter L.jpeg"), newEntry.getBinaryProperty("letter L.jpeg")); - - } - - /** - * Checks that a new database can add binary properties saves and reloads correctly - */ - @Test - public void createAndSaveCheck() throws IOException { - Path file = Files.createTempFile("keepass", "tmp"); - Database database1 = newDatabase(); - Entry entry = database1.newEntry("Test attachment"); - database1.getRootGroup().addEntry(entry); - InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); - assert testFile != null; - byte [] letterJ = ByteStreams.toByteArray(testFile); - entry.setBinaryProperty("letter J.jpeg", letterJ); - saveDatabase(database1, getCreds("123".getBytes()), Files.newOutputStream(file)); - - Database db = loadDatabase(getCreds("123".getBytes()),Files.newInputStream(file)); - Entry entry1 = db.findEntries("Test attachment").get(0); - // just one property - assertArrayEquals(new String[] {"letter J.jpeg"}, entry1.getBinaryPropertyNames().toArray()); - // content is correct - assertArrayEquals(letterJ, entry1.getBinaryProperty("letter J.jpeg")); - } - - -} diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java deleted file mode 100644 index 43b9ee69..00000000 --- a/test/src/test/java/org/linguafranca/pwdb/checks/PropertyValueChecks.java +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.checks;/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import org.junit.BeforeClass; -import org.junit.Test; -import org.linguafranca.pwdb.*; - -import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Consumer; - -import static org.junit.Assert.*; - -/** - * Testing the operation of PropertyValue mechanisms - */ -public abstract class PropertyValueChecks { - - public abstract void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; - public abstract Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; - public abstract Credentials getCreds(byte[] creds); - - private final boolean propertyValueSupported; - protected Database database; - - @BeforeClass - public static void ensureOutputDir() throws IOException { - Files.createDirectories(Paths.get("testOutput")); - } - - public PropertyValueChecks(boolean propertyValueSupported) throws IOException { - this.database = createDatabase(); - this.propertyValueSupported = propertyValueSupported; - } - - public abstract Database createDatabase() throws IOException; - - /** - * Catches expected thrown exception - * - * @param f a Consumer i.e. a function that does have output - * @param c the exception that is expected to be thrown when the function is executed - */ - private void catchException(Consumer f, @SuppressWarnings("SameParameterValue") Class c) { - try { - f.accept(null); - } catch (Exception e) { - if (c.equals(e.getClass())) { - return; - } - } - fail(c.getName() + " expected"); - } - - /** - * Check that unsupported operations throw exceptions - */ - @Test - public void noPropertyValueSupport() { - if (propertyValueSupported) return; - - assertFalse(database.supportsPropertyValueStrategy()); - // test a sample of the methods that are supposed to thrown if used here - catchException((z) -> database.setPropertyValueStrategy(null), UnsupportedOperationException.class); - catchException((z) -> database.getPropertyValueStrategy(), UnsupportedOperationException.class); - catchException((z) -> database.setShouldProtect(null, true), UnsupportedOperationException.class); - catchException((z) -> database.listShouldProtect(), UnsupportedOperationException.class); - - Entry entry = database.newEntry(); - catchException((z) -> entry.getPropertyValue(null), UnsupportedOperationException.class); - catchException((z) -> entry.setPropertyValue(null, (PropertyValue) null), UnsupportedOperationException.class); - - } - - @Test - public void checkDefaults(){ - // these are the defaults established by createDatabase() - assertTrue(database.shouldProtect(Entry.STANDARD_PROPERTY_NAME_PASSWORD)); - assertFalse(database.shouldProtect(Entry.STANDARD_PROPERTY_NAME_USER_NAME)); - assertFalse(database.shouldProtect(Entry.STANDARD_PROPERTY_NAME_NOTES)); - assertFalse(database.shouldProtect(Entry.STANDARD_PROPERTY_NAME_URL)); - assertFalse(database.shouldProtect(Entry.STANDARD_PROPERTY_NAME_TITLE)); - } - - @Test - public void expectedStorageType() throws IOException { - if (!propertyValueSupported) { - return; - } - - // Password is protected - PropertyValue.Strategy pvs = database.getPropertyValueStrategy(); - PropertyValue pv = pvs.getFactoryFor("Password").of("123"); - assertEquals(pv.getClass(), pvs.newProtected().of("a").getClass()); - assertTrue(pv.isProtected()); - - // Notes isn't protected - pv = pvs.getFactoryFor("Notes").of("123"); - assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); - assertFalse(pv.isProtected()); - - // "random" is not protected - pv = pvs.getFactoryFor("random").of("123"); - assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); - assertFalse(pv.isProtected()); - - // create a strategy that makes "random" protected - PropertyValue.Strategy newPvs = new PropertyValue.Strategy() { - @Override - public List getProtectedProperties() { - List pp = new ArrayList<>(pvs.getProtectedProperties()); - pp.add("random"); - return pp; - } - - @Override - public PropertyValue.Factory newProtected() { - return pvs.newProtected(); - } - - @Override - public PropertyValue.Factory newUnprotected() { - return pvs.newUnprotected(); - } - }; - database.setPropertyValueStrategy(newPvs); - - // get a value for random and check it is protected - pv = newPvs.getFactoryFor("random").of("123"); - assertEquals(pv.getClass(), pvs.newProtected().of("b").getClass()); - assertTrue(pv.isProtected()); - - // create an entry with random property as protected value - Entry entry = database.newEntry("Test Random"); - entry.setPropertyValue("random", pv); - database.getRootGroup().addEntry(entry); - - // Save database - - FileOutputStream fos = new FileOutputStream("testOutput/test9.kdbx"); - saveDatabase(database, getCreds("123".getBytes()), fos); - fos.flush(); - fos.close(); - - // reload database, "random" is still protected even though it's not protected by default - FileInputStream fis = new FileInputStream("testOutput/test9.kdbx"); - Database input = loadDatabase(getCreds("123".getBytes()), fis); - - List entries = input.findEntries("random"); - assertEquals(1, entries.size()); - assertTrue(entries.get(0).getPropertyValue("random").isProtected()); - assertFalse(input.getPropertyValueStrategy().getProtectedProperties().contains("random")); -} - -} diff --git a/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java b/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java deleted file mode 100644 index b76b7986..00000000 --- a/test/src/test/java/org/linguafranca/pwdb/checks/RecycleBinChecks.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.checks; - -import org.junit.Test; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Group; - -import static org.junit.Assert.*; - -/** - * @author jo - */ -public abstract class RecycleBinChecks { - - protected Database database; - - @Test - public void recycleBinEntries() { - database.enableRecycleBin(false); - assertFalse(database.isRecycleBinEnabled()); - assertNull(database.getRecycleBin()); - database.enableRecycleBin(true); - assertTrue(database.isRecycleBinEnabled()); - Group recycleBin = database.getRecycleBin(); - assertNotNull(recycleBin); - - Entry entry = database.newEntry(); - assertEquals(entry, database.getRootGroup().addEntry(entry)); - assertEquals(1, database.getRootGroup().getEntriesCount()); - - assertTrue(database.deleteEntry(entry.getUuid())); - assertEquals(0, database.getRootGroup().getEntriesCount()); - assertEquals(1, recycleBin.getEntriesCount()); - - database.enableRecycleBin(false); - assertFalse(database.isRecycleBinEnabled()); - - entry = database.newEntry(); - assertEquals(entry, database.getRootGroup().addEntry(entry)); - assertEquals(1, database.getRootGroup().getEntriesCount()); - - assertTrue(database.deleteEntry(entry.getUuid())); - assertEquals(0, database.getRootGroup().getEntriesCount()); - assertEquals(1, recycleBin.getEntriesCount()); - database.emptyRecycleBin(); - assertEquals(0, recycleBin.getEntriesCount()); - } - - @Test - public void recycleBinGroups() { - database.enableRecycleBin(true); - assertTrue(database.isRecycleBinEnabled()); - Group recycleBin = database.getRecycleBin(); - assertNotNull(recycleBin); - - Group group= database.newGroup(); - assertEquals(group, database.getRootGroup().addGroup(group)); - assertEquals(2, database.getRootGroup().getGroupsCount()); - - assertTrue(database.deleteGroup(group.getUuid())); - assertEquals(1, database.getRootGroup().getGroupsCount()); - assertEquals(1, recycleBin.getGroupsCount()); - - database.enableRecycleBin(false); - assertFalse(database.isRecycleBinEnabled()); - - group = database.newGroup(); - assertEquals(group, database.getRootGroup().addGroup(group)); - assertEquals(2, database.getRootGroup().getGroupsCount()); - - assertTrue(database.deleteGroup(group.getUuid())); - assertEquals(1, database.getRootGroup().getGroupsCount()); - assertEquals(1, recycleBin.getGroupsCount()); - database.emptyRecycleBin(); - assertEquals(0, recycleBin.getGroupsCount()); - } - -} diff --git a/util/pom.xml b/util/pom.xml deleted file mode 100644 index 4e44359a..00000000 --- a/util/pom.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - 4.0.0 - - KeePassJava2.parent - org.linguafranca.pwdb - 3.0.0-SNAPSHOT - ../pom.xml - - - util - PWDB :: Util - Contains some helpful underlying classes for examples and testing. - - - true - - - - - org.slf4j - slf4j-api - provided - - - org.junit.vintage - junit-vintage-engine - 5.12.0 - provided - - - \ No newline at end of file From 62171bcd576e37486b586607ab06f8c00b4e5e7f Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Wed, 12 Mar 2025 21:28:23 +0000 Subject: [PATCH 44/66] attending to nits from the "find problems with this project" --- .../pwdb/basic/BasicDatabase.java | 2 +- .../pwdb/basic/BasicDatabaseSerializer.java | 2 +- .../java/org/linguafranca/pwdb/Entry.java | 2 -- .../org/linguafranca/pwdb/security/Aes.java | 5 ++++ .../pwdb/test/GroupsAndEntriesTest.java | 11 +++++---- .../pwdb/test/KdbxFileSaveAndReloadTest.java | 23 +++++++++--------- .../pwdb/test/ProtectedPropertyTest.java | 2 -- .../pwdb/test/ProtectedPropertyTest2.java | 3 --- .../linguafranca/pwdb/test/Test123Test.java | 24 +++++++------------ .../pwdb/test/TrivialDatabaseTest.java | 11 --------- .../linguafranca/pwdb/kdb/KdbDatabase.java | 1 + .../linguafranca/pwdb/kdb/KdbSerializer.java | 18 +++++++------- .../pwdb/kdb/KdbDatabaseTest.java | 2 -- .../pwdb/kdbx/jackson/KdbxDatabase.java | 5 ---- .../pwdb/kdbx/jackson/KdbxEntry.java | 3 --- .../pwdb/kdbx/jackson/KdbxGroup.java | 1 - .../jackson/KdbxSerializableDatabase.java | 3 ++- .../jackson/converter/ValueSerializer.java | 4 +--- .../linguafranca/pwdb/format/KdbxCreds.java | 3 --- .../pwdb/format/KdbxSerializer.java | 3 +-- .../hashedblock/HashedBlockInputStream.java | 2 +- .../hashedblock/HashedBlockOutputStream.java | 4 ++-- .../hashedblock/HmacBlockInputStream.java | 19 ++------------- .../hashedblock/HmacBlockOutputStream.java | 2 -- .../linguafranca/pwdb/kdbx/HelperTest.java | 2 +- .../pwdb/kdbx/KdbxHeaderTest.java | 1 - .../pwdb/kdbx/KdbxKeyFileTest.java | 2 +- readme.md | 11 ++++----- 28 files changed, 59 insertions(+), 112 deletions(-) diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 96f37d39..f04cde9e 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -146,5 +146,5 @@ public boolean isEntriesFirst() { return false; } - }; + } } diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java index 3f707761..a671b730 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java @@ -230,7 +230,7 @@ protected static class BasicEntry {} protected List entries; @JsonPropertyOrder({ "uuid", "name", "icon", "group", "entry" }) - protected static class BasicGroup {}; + protected static class BasicGroup {} @JacksonXmlProperty(localName = "group") @JacksonXmlElementWrapper(useWrapping = false) diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index c96efc97..46c90fdb 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -323,7 +323,6 @@ interface Matcher { * @return a password * @deprecated use {@link #getPropertyValue(String)} with {@link #STANDARD_PROPERTY_NAME_PASSWORD} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated String getPassword(); @@ -335,7 +334,6 @@ interface Matcher { * @param pass a password * @deprecated use {@link #setPropertyValue(String, PropertyValue)} with {@link #STANDARD_PROPERTY_NAME_PASSWORD} */ - @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated void setPassword(String pass); diff --git a/database/src/main/java/org/linguafranca/pwdb/security/Aes.java b/database/src/main/java/org/linguafranca/pwdb/security/Aes.java index 51afece8..529b07c2 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/Aes.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/Aes.java @@ -39,7 +39,12 @@ * AES may be used for Key Derivation and also as the underlying stream cipher *

* The class is a singleton + * + * {@implNote This class is a singleton} + * {@implNote Warnings for the use of AES CBC etc. noted, however their continuing use is required for + * compatibility / interoperability with existing databases} */ +@SuppressWarnings({"deprecation"}) public class Aes implements CipherAlgorithm, KeyDerivationFunction { /** UUID specifying that AES is to be used as the Key Derivation Function in KDBX */ diff --git a/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java index d8ee136e..05bb232b 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java @@ -44,6 +44,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.*; +import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.PASSWORD; /** * @author Jo @@ -130,7 +131,7 @@ default void testSetFields () { e1.setUsername("jake@window.com"); assertEquals("jake@window.com", e1.getUsername()); e1.setPassword("supercalifragelisticexpialidocious"); - assertEquals("supercalifragelisticexpialidocious", e1.getPassword()); + assertEquals("supercalifragelisticexpialidocious", e1.getProperty(PASSWORD)); e1.setUrl("https://window.com"); assertEquals("https://window.com", e1.getUrl()); @@ -151,7 +152,7 @@ default void testSetFields () { e1.setProperty(Entry.STANDARD_PROPERTY_NAME_NOTES, "notes"); assertEquals("notes", e1.getNotes()); e1.setProperty(Entry.STANDARD_PROPERTY_NAME_PASSWORD, "password"); - assertEquals("password", e1.getPassword()); + assertEquals("password", e1.getProperty(PASSWORD)); e1.setProperty(Entry.STANDARD_PROPERTY_NAME_URL, "url"); assertEquals("url", e1.getUrl()); @@ -230,7 +231,7 @@ default void checkAddChangeRemoveProperty() { default void testNewEntryInit() { Entry e2 = getDatabase().newEntry(); assertNull(e2.getParent()); - assertEquals("", e2.getPassword()); + assertEquals("", e2.getProperty(PASSWORD)); assertNotNull(e2.getUuid()); assertEquals("", e2.getUrl()); assertEquals("", e2.getNotes()); @@ -247,7 +248,7 @@ default void testCopy() throws IOException { Entry entry1 = getDatabase().newEntry(); entry1.setTitle("Entry"); entry1.setUsername("Username"); - entry1.setPassword("Password"); + entry1.setProperty(PASSWORD, "Password"); entry1.setUrl("https://dont.follow.me"); entry1.setNotes("Notes"); entry1.setIcon(getDatabase().newIcon(2)); @@ -259,7 +260,7 @@ default void testCopy() throws IOException { assertEquals(entry1.getTitle(), entry2.getTitle()); assertEquals(entry1.getUsername(), entry2.getUsername()); - assertEquals(entry1.getPassword(), entry2.getPassword()); + assertEquals(entry1.getProperty(PASSWORD), entry2.getProperty(PASSWORD)); assertEquals(entry1.getUrl(), entry2.getUrl()); assertEquals(entry1.getNotes(), entry2.getNotes()); assertEquals(entry1.getIcon(), entry2.getIcon()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java index 682dce7f..a8b7ab85 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java @@ -42,6 +42,7 @@ import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; +import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -87,8 +88,6 @@ default void saveAndReloadTest() throws IOException { FileInputStream fis = new FileInputStream("testOutput/test1.kdbx"); Database input = loadDatabase(getCredentials("123".getBytes()), fis); verifyContents(input); - //input.save(new StreamFormat.None(), new Credentials.None(), printStream); - //getTestPrintStream().format("Test took %d millis", System.currentTimeMillis() - now); } /** @@ -162,11 +161,11 @@ private Database createNewDatabase() throws IOException { private Entry entryFactory(Database database, String g, int e) { Entry result = database.newEntry(); - result.setTitle(g + "-" + e); - result.setUsername(g + " - un - " + e); - result.setPassword(g + "- p -" + e); - result.setUrl(g + "- url - " + e); - result.setNotes(g + "- n - " + e); + result.addProperty(TITLE, g + "-" + e) + .addProperty(USER_NAME, g + " - un - " + e) + .addProperty(PASSWORD, g + "- p -" + e) + .addProperty(URL,g + "- url - " + e) + .addProperty(NOTES,g + "- n - " + e); return result; } @@ -181,7 +180,7 @@ private void verifyContents(Database database) { Entry entry = group.getEntries().get(e); assertEquals(g + "-" + e, entry.getTitle()); assertEquals(g + " - un - " + e, entry.getUsername()); - assertEquals(g + "- p -" + e, entry.getPassword()); + assertEquals(g + "- p -" + e, entry.getProperty(PASSWORD)); assertEquals(g + "- url - " + e, entry.getUrl()); assertEquals(g + "- n - " + e, entry.getNotes()); assertEquals(group, entry.getParent()); @@ -286,12 +285,12 @@ default void testNewDatabase() throws IOException { group2.addEntry(entry1); assertEquals(1, group2.getEntries().size()); - entry1.setPassword("pass"); - assertEquals("pass", entry1.getPassword()); + entry1.addProperty(PASSWORD, "pass"); + assertEquals("pass", entry1.getProperty(PASSWORD)); Entry entry2 = database.newEntry(entry1); - entry2.setPassword("pass2"); - assertEquals("pass2", entry2.getPassword()); + entry2.addProperty(PASSWORD,"pass2"); + assertEquals("pass2", entry2.getProperty(PASSWORD)); group2.addEntry(entry2); assertEquals(2, group2.getEntries().size()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java index ad346f83..2783df77 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java @@ -18,11 +18,9 @@ package org.linguafranca.pwdb.test; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.*; -import java.util.ArrayList; import java.util.List; import static org.junit.jupiter.api.Assertions.*; diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java index 2f2d08ab..139627e3 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java @@ -17,7 +17,6 @@ package org.linguafranca.pwdb.test; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; @@ -26,8 +25,6 @@ import org.linguafranca.pwdb.PropertyValue; import java.io.*; -import java.nio.file.Files; -import java.nio.file.Paths; import java.util.List; import java.util.function.Consumer; diff --git a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java index d8ae6e30..21391c69 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java @@ -29,6 +29,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.PASSWORD; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** @@ -61,16 +62,11 @@ default void test123File() throws ParseException { getTestPrintStream().println(tes.getTitle()); } assertEquals(4, tests.size()); - if (tests.size() > 0) { - // copy the password of the first entry to the clipboard - String pass = tests.get(0).getPassword(); -/* - StringSelection selection = new StringSelection(pass); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, selection); - printStream.println(pass + " copied to clip board"); -*/ + if (!tests.isEmpty()) { + // copy the password of the first entry + String pass = tests.get(0).getProperty(PASSWORD); // all the relevant entries should have the password 123 - String pass2 = tests.get(0).getPassword(); + String pass2 = tests.get(0).getProperty(PASSWORD); assertEquals(pass, pass2); assertEquals("123", pass2); } @@ -78,18 +74,14 @@ default void test123File() throws ParseException { List passwords = getDatabase().findEntries("password"); assertEquals(4, passwords.size()); for (Entry passwordEntry : passwords) { - assertEquals(passwordEntry.getTitle(), passwordEntry.getPassword()); + assertEquals(passwordEntry.getTitle(), passwordEntry.getProperty(PASSWORD)); getTestPrintStream().println(passwordEntry.getTitle()); } - List entries = getDatabase().findEntries(new Entry.Matcher() { - @Override - public boolean matches(Entry entry) { - return entry.getTitle().equals("hello world"); - }}); + List entries = getDatabase().findEntries(entry -> entry.getTitle().equals("hello world")); assertEquals(1, entries.size()); - assertEquals("pass", entries.get(0).getPassword()); + assertEquals("pass", entries.get(0).getProperty(PASSWORD)); // kdb files don't have a time zone so can't make head or tail of the date - test file seems to have a local time in it if (getSkipDateCheck()) { diff --git a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java index 5f34bbe1..a9288dbb 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java @@ -117,17 +117,6 @@ default void testDeleteGroup() { assertNull(getDatabase().findGroup(uuid)); } -/* @Test - default void testSave() throws IOException { - OutputStream outputStream = new OutputStream() { - @Override - public void write(int b) { - // no-op - } - }; - assertThrows(IOException.class, () -> getDatabase().save(null, outputStream)); - }*/ - @Test default void testSupportsNonStandardPropertyNames() { // check standard names are there diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java index 11c2575b..dfc759e8 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java @@ -171,6 +171,7 @@ public boolean supportsBinaryProperties() { return false; } + @SuppressWarnings("RedundantMethodOverride") @Override public boolean supportsRecycleBin() { return false; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java index 07afd79c..0883ad16 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java @@ -84,7 +84,7 @@ public static KdbDatabase createKdbDatabase(Credentials credentials, KdbHeader k // read the decrypted serialized form of all groups KdbDatabase kdbDatabase = new KdbDatabase(); - KdbGroup lastGroup = (KdbGroup) kdbDatabase.getRootGroup(); + KdbGroup lastGroup = kdbDatabase.getRootGroup(); for (long group = 0; group < kdbHeader.getGroupCount(); group++) { lastGroup = deserializeGroup(lastGroup, dataInput); } @@ -120,7 +120,7 @@ private static void setDatabase(KdbDatabase kdbDatabase, KdbGroup group) { * Check the signature of a data source * * @param dataInput the source to check - * @throws IOException + * @throws IOException if an integer could not be read from buffer * @throws IllegalStateException if the signature does not match */ public static void checkSignature(DataInput dataInput) throws IOException { @@ -134,7 +134,7 @@ public static void checkSignature(DataInput dataInput) throws IOException { * * @param kdbHeader a header to populate with relevant values * @param dataInput a source of data - * @throws IOException + * @throws IOException if there is a problem reading from the source */ private static void deserializeHeader(KdbHeader kdbHeader, DataInput dataInput) throws IOException { kdbHeader.setFlags(dataInput.readInt()); @@ -168,7 +168,7 @@ private static void deserializeHeader(KdbHeader kdbHeader, DataInput dataInput) * @param lastGroup the last group loaded from this source, or the root group if none * @param dataInput a source of data * @return a new KdbxGroup - * @throws IOException + * @throws IOException if there is a problem reading from the source */ private static KdbGroup deserializeGroup(KdbGroup lastGroup, DataInput dataInput) throws IOException { int fieldType; @@ -209,7 +209,7 @@ private static KdbGroup deserializeGroup(KdbGroup lastGroup, DataInput dataInput group.setFlags(readInt(dataInput)); break; default: - throw new IllegalStateException("Unknown field type " + String.valueOf(fieldType)); + throw new IllegalStateException("Unknown field type " + fieldType); } } dataInput.readInt(); @@ -221,7 +221,7 @@ private static KdbGroup deserializeGroup(KdbGroup lastGroup, DataInput dataInput * * @param database a database to insert the entry into * @param dataInput a source of data - * @throws IOException + * @throws IOException if there is a problem reading from the source */ private static void deserializeEntry(KdbDatabase database, DataInput dataInput) throws IOException { int fieldType; @@ -318,10 +318,11 @@ private static KdbGroup computeParentGroup(KdbGroup lastGroup, int level) { throw new IllegalStateException("Could not determine parent group from level supplied"); } // working variable holding current candidate parent - KdbGroup candidateParent = (KdbGroup) lastGroup.getParent(); + KdbGroup candidateParent = lastGroup.getParent(); // work our way up through the parents till we find one + //noinspection DataFlowIssue - you know - if it's broken it's broken while (level <= candidateParent.computedLevel()) { - candidateParent = ((KdbGroup) candidateParent.getParent()); + candidateParent = (candidateParent.getParent()); } return candidateParent; } @@ -421,6 +422,7 @@ private static byte[] readBuffer(DataInput dataInput) throws IOException { return buffer; } + @SuppressWarnings("UnusedReturnValue") private static byte[] readExtData(DataInput dataInput) throws IOException { int size = dataInput.readInt(); byte[] buffer = new byte[size]; diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java index 9604027b..f1e30925 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java @@ -21,8 +21,6 @@ import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.test.GroupsAndEntriesTest; -import java.io.IOException; - import static org.junit.Assert.assertFalse; /** diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index d322a498..dbc6b886 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -194,11 +194,6 @@ public void setDescription(String s) { setDirty(true); } -/* @Override - public boolean shouldProtect(String s) { - return keePassFile.meta.memoryProtection.shouldProtect(s); - }*/ - public List getBinaries() { if (keePassFile.meta.binaries == null) { keePassFile.createBinaries(); diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index f9427f3b..7cc94866 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -36,9 +36,6 @@ import org.linguafranca.pwdb.kdbx.jackson.model.KeePassFile; import org.linguafranca.pwdb.kdbx.jackson.model.Times; -import org.linguafranca.pwdb.kdbx.jackson.model.EntryClasses.AutoType; -import org.linguafranca.pwdb.kdbx.jackson.model.EntryClasses.BinaryProperty; -import org.linguafranca.pwdb.kdbx.jackson.model.EntryClasses.StringProperty; import static org.linguafranca.pwdb.kdbx.jackson.model.EntryClasses.*; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java index 7721c933..c8a58f3c 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxGroup.java @@ -24,7 +24,6 @@ import com.fasterxml.jackson.databind.annotation.JsonSerialize; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper; import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty; -import org.jetbrains.annotations.NotNull; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Icon; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index ccd8251d..68e6b786 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -93,7 +93,7 @@ public KdbxSerializableDatabase load(InputStream inputStream) throws IOException public void save(OutputStream outputStream) { try { SimpleModule module = new SimpleModule(); - module.addSerializer(PropertyValue.class, new ValueSerializer(encryptor, propertyValueStrategy)); + module.addSerializer(PropertyValue.class, new ValueSerializer(encryptor)); // disable auto-detection, only use annotated values XmlMapper mapper = XmlMapper.builder() .disable(MapperFeature.AUTO_DETECT_CREATORS, @@ -117,6 +117,7 @@ public void save(OutputStream outputStream) { OutputStreamWriter osw = new OutputStreamWriter(outputStream); XMLStreamWriter sw = xmlOutputFactory.createXMLStreamWriter(osw); + //noinspection TryFinallyCanBeTryWithResources - doesn't work with XMLStreamWriter try { sw.setPrefix("xml", "http://www.w3.org/XML/1998/namespace"); diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java index ce0fb5b4..ab98e10e 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/ValueSerializer.java @@ -30,12 +30,10 @@ public class ValueSerializer extends StdSerializer { private final StreamEncryptor encryptor; - private final PropertyValue.Strategy propertyValueStrategy; - public ValueSerializer(StreamEncryptor encryptor, PropertyValue.Strategy propertyValueStrategy) { + public ValueSerializer(StreamEncryptor encryptor) { super(ValueSerializer.class, false); this.encryptor = encryptor; - this.propertyValueStrategy = propertyValueStrategy; } private String encrypt(byte[] bytes) { diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java index 9577f983..4d218c28 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java @@ -18,11 +18,8 @@ package org.linguafranca.pwdb.format; import org.jetbrains.annotations.NotNull; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.security.Encryption; import java.io.InputStream; -import java.security.MessageDigest; /** * Convenience wrapper to convert to KdbxCredentials - for helping with code migration to version 3. diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java index bedb5bb8..49ce6fea 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java @@ -83,7 +83,7 @@ * @see this diagram * @author jo */ -@SuppressWarnings({"WeakerAccess", "UnstableApiUsage"}) +@SuppressWarnings({"WeakerAccess"}) public class KdbxSerializer { // make entirely static @@ -378,7 +378,6 @@ private static void getOuterHeaderFields(KdbxHeader kdbxHeader, DataInput input) * @param kdbxHeader the header to write and update * @throws IOException on error */ - @SuppressWarnings("UnstableApiUsage") public static void writeKdbxHeader(KdbxHeader kdbxHeader, OutputStream outputStream) throws IOException { MessageDigest messageDigest = Encryption.getSha256MessageDigestInstance(); DigestOutputStream digestOutputStream = new DigestOutputStream(outputStream, messageDigest); diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java index e3b30e2a..6d17d5c4 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java @@ -57,7 +57,7 @@ public class HashedBlockInputStream extends InputStream { private long expectedSequenceNumber = 0; private boolean littleEndian = false; private boolean done = false; - private InputStream inputStream; + private final InputStream inputStream; private ByteArrayInputStream blockInputStream = new ByteArrayInputStream(new byte[0]); /** diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java index 283d4b95..5fa2e6e6 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java @@ -55,8 +55,8 @@ public class HashedBlockOutputStream extends OutputStream { private final MessageDigest sha256; private int nextSequenceNumber = 0; private boolean littleEndian = false; - private OutputStream outputStream; - private ByteArrayOutputStream blockOutputStream = new ByteArrayOutputStream(); + private final OutputStream outputStream; + private final ByteArrayOutputStream blockOutputStream = new ByteArrayOutputStream(); private boolean isClosed = false; /** diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java index f411197f..f4d22fad 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java @@ -18,7 +18,6 @@ package org.linguafranca.pwdb.hashedblock; import com.google.common.io.LittleEndianDataInputStream; -import org.jetbrains.annotations.NotNull; import org.linguafranca.pwdb.format.Helpers; import javax.crypto.Mac; @@ -26,7 +25,6 @@ import java.nio.ByteOrder; import java.util.Arrays; -import static org.linguafranca.pwdb.format.Helpers.toBytes; import static org.linguafranca.pwdb.security.Encryption.getHMacSha256Instance; import static org.linguafranca.pwdb.security.Encryption.transformHmacKey; @@ -124,28 +122,15 @@ private void verifyHmac(byte[] buffer, long blockNumber, byte[] hmacSha256) { if (!Arrays.equals(mac.doFinal(buffer), hmacSha256)) { throw new IllegalStateException("Block HMAC does not match"); } - -/* // using bouncy castle - HMac hmac = new HMac(new SHA256Digest()); - hmac.init(new KeyParameter(getHmacBlockKey(this.key, blockNumber, byteOrder))); - hmac.update(Helpers.toBytes(blockNumber, byteOrder), 0, 8); - hmac.update(Helpers.toBytes(buffer.length, byteOrder), 0, 4); - hmac.update(buffer, 0, buffer.length); - byte[] computedHmacSha256 = new byte[32]; - hmac.doFinal(computedHmacSha256, 0); - if (!Arrays.equals(computedHmacSha256, hmacSha256)) { - throw new IllegalStateException("Block HMAC does not match"); - } -*/ } @Override - public int read(@NotNull byte[] b) throws IOException { + public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override - public int read(@NotNull byte[] b, int off, int len) throws IOException { + public int read(byte[] b, int off, int len) throws IOException { if (finished) { return -1; } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java index 6dfc22a7..7e0fbaca 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java @@ -17,7 +17,6 @@ package org.linguafranca.pwdb.hashedblock; -import static org.linguafranca.pwdb.format.Helpers.toBytes; import static org.linguafranca.pwdb.security.Encryption.getHMacSha256Instance; import static org.linguafranca.pwdb.security.Encryption.transformHmacKey; @@ -78,7 +77,6 @@ public HmacBlockOutputStream(byte [] key, OutputStream outputStream, boolean lit this.key = key; this.byteOrder = littleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; if (this.byteOrder.equals(ByteOrder.LITTLE_ENDIAN)) { - //noinspection UnstableApiUsage this.output = new LittleEndianDataOutputStream(out); } else { this.output = new DataOutputStream(out); diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java index 5c66459a..f7853ed2 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java @@ -42,7 +42,7 @@ public void testBase64() throws Exception { assertEquals(hexString, convertedString.toUpperCase()); UUID uuid = Helpers.uuidFromBase64(inputString); - printStream.println("UUID: " + uuid.toString()); + printStream.println("UUID: " + uuid); assertEquals("d2d53c5c-5454-5f64-c293-9b669abb21b9", uuid.toString()); String uuidHex = Helpers.hexStringFromUuid(uuid); diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java index d0bc5c7b..a438e561 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java @@ -48,7 +48,6 @@ public void getHmacStream() throws IOException { printStream.println("Version " + header.getVersion()); KdbxCredentials creds = new KdbxCredentials("123".getBytes()); assert inputStream != null; - //noinspection UnstableApiUsage KdbxSerializer.readOuterHeaderVerification(header, creds, new LittleEndianDataInputStream(inputStream)); HmacBlockInputStream hmacBlockInputStream = new HmacBlockInputStream(header.getHmacKey(creds), inputStream, true); printStream.println(CharStreams.toString(new InputStreamReader(hmacBlockInputStream, StandardCharsets.UTF_8))); diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java index 0db4fc9d..91b0c6ca 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java @@ -53,7 +53,7 @@ public void testLoad() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("KeyFileDatabase.key"); byte[] key = KdbxKeyFile.load(inputStream); assertNotNull(key); - assertEquals(32, key.length);; + assertEquals(32, key.length); } /** diff --git a/readme.md b/readme.md index ce22349a..276a768b 100644 --- a/readme.md +++ b/readme.md @@ -227,9 +227,6 @@ Each module corresponds to a Maven artifact. The GroupId is `org.linguafranca.pw Javadocs Worked examples of loading, saving, splicing etc. using the APIs -testtest -Javadocs -Shared tests to assess the viability of the implementation. allKeePassJava2 (no JavaDoc) @@ -248,14 +245,16 @@ Each module corresponds to a Maven artifact. The GroupId is `org.linguafranca.pw Javadocs Provides support for KDBX data access and memory protection. +basicbasic + +Javadocs +A basic lightweight database implementation. Has memory protection. ### Gradle -If you prefer Gradle the automatic conversion `gradle init` has been known to convert the POM successfully, however you will -need to add something like [gradle-source-sets.txt](jaxb/gradle-source-sets.txt) to the `build.gradle` for the JAXB module, so that the generated sources - get compiled correctly. +If you prefer Gradle the automatic conversion `gradle init` has been known to convert the POM successfully. ## Change Log From e51e481df34082f9cf95d0cb83e1874c990dfb6e Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Wed, 12 Mar 2025 22:15:05 +0000 Subject: [PATCH 45/66] remove all except MavenRunListener from Junit4 --- database/pom.xml | 78 ++++++++++--------- .../linguafranca/pwdb/PropertyValueTest.java | 7 +- .../linguafranca/pwdb/PropertyValueUtil.java | 4 +- .../pwdb/test/GroupsAndEntriesTest.java | 2 +- .../SecureCharBufferPropertyValueTest.java | 3 +- .../linguafranca/pwdb/kdbx/SaxParseTest.java | 2 +- .../pwdb/kdbx/SimpleQuickStartTest.java | 8 +- .../pwdb/kdb/KdbDatabaseTest.java | 5 +- .../pwdb/kdb/KdbSerializerTest.java | 2 +- .../linguafranca/pwdb/kdb/KeyFileTest.java | 4 +- .../kdbx/database/KdbxDatabaseLoadTest.java | 2 +- .../pwdb/kdbx/database/util/UtilTest.java | 6 +- .../kdbx/database/validation/Issue27Test.java | 8 +- .../kdbx/database/validation/Issue33Test.java | 11 +-- .../kdbx/database/validation/Issue38Test.java | 2 +- .../kdbx/database/validation/Issue70Test.java | 2 +- .../pwdb/hashedblock/HashBlockTest.java | 6 +- .../linguafranca/pwdb/kdbx/HelperTest.java | 4 +- .../linguafranca/pwdb/kdbx/HelpersTest.java | 4 +- .../pwdb/kdbx/KdbxHeaderTest.java | 8 +- .../pwdb/kdbx/KdbxKeyFileTest.java | 45 ++++++----- .../pwdb/kdbx/KdbxSerializerTest.java | 4 +- .../pwdb/security/StreamEncryptionTest.java | 4 +- .../resources/kdbx_hash_test_wrong_hash.keyx | 0 pom.xml | 5 -- 25 files changed, 118 insertions(+), 108 deletions(-) rename {kdbx-database => kdbx-io}/src/test/resources/kdbx_hash_test_wrong_hash.keyx (100%) diff --git a/database/pom.xml b/database/pom.xml index 319d3f20..35cc4b37 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -16,43 +16,49 @@ ~ --> - - - KeePassJava2.parent - org.linguafranca.pwdb - 3.0.0-SNAPSHOT - ../pom.xml - - 4.0.0 + + + KeePassJava2.parent + org.linguafranca.pwdb + 3.0.0-SNAPSHOT + ../pom.xml + + 4.0.0 - database - PWDB :: Database - Contains root definitions of PWDB Databases etc. + database + PWDB :: Database + Contains root definitions of PWDB Databases etc. - - - com.google.guava - guava - - - org.bouncycastle - bcpkix-jdk18on - - + + + com.google.guava + guava + + + org.bouncycastle + bcpkix-jdk18on + + + org.junit.vintage + junit-vintage-engine + test + + - - - - org.apache.maven.plugins - maven-jar-plugin - - - - test-jar - - - - - - + + + + org.apache.maven.plugins + maven-jar-plugin + + + + test-jar + + + + + + \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java index 578e3ab5..070eb210 100644 --- a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java @@ -17,11 +17,14 @@ package org.linguafranca.pwdb; -import org.junit.Test; + + +import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.*; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.linguafranca.pwdb.PropertyValueUtil.genericTest; public class PropertyValueTest { diff --git a/database/src/test/java/org/linguafranca/pwdb/PropertyValueUtil.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueUtil.java index ab071e6c..ee85aae9 100644 --- a/database/src/test/java/org/linguafranca/pwdb/PropertyValueUtil.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueUtil.java @@ -19,8 +19,8 @@ import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Make available as a utility to provide for PropertyValue extension implementations diff --git a/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java index 05bb232b..3e6dcec9 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java @@ -130,7 +130,7 @@ default void testSetFields () { assertEquals("this looks a little like Entry 2", e1.getNotes()); e1.setUsername("jake@window.com"); assertEquals("jake@window.com", e1.getUsername()); - e1.setPassword("supercalifragelisticexpialidocious"); + e1.setProperty(PASSWORD, "supercalifragelisticexpialidocious"); assertEquals("supercalifragelisticexpialidocious", e1.getProperty(PASSWORD)); e1.setUrl("https://window.com"); assertEquals("https://window.com", e1.getUrl()); diff --git a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java index f9cdfb3b..d78f7f1c 100644 --- a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java @@ -17,7 +17,8 @@ package org.linguafranca.pwdb.example; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import static org.linguafranca.pwdb.PropertyValueUtil.genericTest; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java index c4d153c9..08883a02 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.KdbxCredentials; import org.xml.sax.SAXException; diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index 8e7c5269..d2ee72ec 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -17,8 +17,8 @@ package org.linguafranca.pwdb.kdbx; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; @@ -29,7 +29,7 @@ import java.nio.file.Path; import java.nio.file.Paths; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * Simple illustration of hooking a SAX parser up to process a KDBX file @@ -39,7 +39,7 @@ public class SimpleQuickStartTest extends QuickStart { - @BeforeClass + @BeforeAll public static void ensureOutputDir() throws IOException { Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); } diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java index f1e30925..c8ad7556 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java @@ -17,11 +17,10 @@ package org.linguafranca.pwdb.kdb; -import org.junit.Test; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.test.GroupsAndEntriesTest; - -import static org.junit.Assert.assertFalse; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; /** * @author Jo diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java index 75c1fc58..d040e1e8 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdb; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Visitor; diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java index c07f87c0..ec7a4f5e 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java @@ -17,12 +17,12 @@ package org.linguafranca.pwdb.kdb; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.IOException; import java.io.InputStream; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author jo diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java index e6973d60..4cf6bfa2 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java @@ -19,7 +19,7 @@ import java.io.InputStream; import java.io.PrintStream; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Visitor; import org.linguafranca.pwdb.format.KdbxCredentials; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java index b0084bd7..61b88fb3 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java @@ -17,7 +17,8 @@ package org.linguafranca.pwdb.kdbx.database.util; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.KdbxCredentials; import java.io.IOException; @@ -31,7 +32,8 @@ public class UtilTest { OutputStream outputStream = getTestPrintStream(); - @Test public void listDatabaseTest() throws IOException { + @Test + public void listDatabaseTest() throws IOException { listDatabase("V3-CustomIcon.kdbx", new KdbxCredentials("123".getBytes()), outputStream); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java index 40c86818..2a8b0589 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java @@ -18,8 +18,8 @@ package org.linguafranca.pwdb.kdbx.database.validation; import com.google.common.io.CharStreams; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.format.KdbxCredentials; @@ -34,7 +34,7 @@ import java.nio.charset.StandardCharsets; import java.util.List; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.linguafranca.util.TestUtil.getTestPrintStream; public class Issue27Test { @@ -71,7 +71,7 @@ public void testV4Date() throws IOException { } } - @Test @Ignore +@Disabled public void testIssue27XML() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java index 1aeaf733..0659ec94 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java @@ -18,9 +18,10 @@ package org.linguafranca.pwdb.kdbx.database.validation; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.StreamFormat; import org.linguafranca.pwdb.format.Helpers; @@ -45,14 +46,14 @@ public class Issue33Test { InputStream inputStream; - @BeforeClass + @BeforeAll public static void listXml() throws IOException { Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); Util.listXml(TEST_RESOURCE, CREDENTIALS, new PrintWriter(TEST_XML_FILE)); Helpers.isV4.set(true); } - @Before + @BeforeEach public void refreshInputStream() { inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java index f0dd8cbd..b49f3a04 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx.database.validation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java index d9702b64..7ff77b44 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx.database.validation; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java index 4846da4d..b356e5e8 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/hashedblock/HashBlockTest.java @@ -17,12 +17,12 @@ package org.linguafranca.pwdb.hashedblock; -import org.junit.Test; - +import org.junit.jupiter.api.Test; import java.io.*; import java.util.Arrays; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java index f7853ed2..ddcc8ece 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelperTest.java @@ -17,13 +17,13 @@ package org.linguafranca.pwdb.kdbx; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.Helpers; import java.io.PrintStream; import java.util.UUID; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java index e6676cd2..080b3151 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/HelpersTest.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.Helpers; import java.io.PrintStream; @@ -26,7 +26,7 @@ import java.time.ZonedDateTime; import java.util.Date; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.pwdb.format.Helpers.dateTimeFormatter; import static org.linguafranca.util.TestUtil.getTestPrintStream; diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java index a438e561..854dffb1 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java @@ -19,8 +19,8 @@ import com.google.common.io.CharStreams; import com.google.common.io.LittleEndianDataInputStream; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -32,7 +32,7 @@ import java.io.PrintStream; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** @@ -41,7 +41,7 @@ public class KdbxHeaderTest { static PrintStream printStream = getTestPrintStream(); - @Test @Ignore // can be used for detailed HMAC debugging + @Disabled // can be used for detailed HMAC debugging public void getHmacStream() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java index 91b0c6ca..7d158234 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java @@ -18,12 +18,11 @@ package org.linguafranca.pwdb.kdbx; import com.google.common.io.CharStreams; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxKeyFile; -import org.linguafranca.pwdb.format.KdbxSerializer; import java.io.IOException; import java.io.InputStream; @@ -31,8 +30,8 @@ import java.io.PrintStream; import java.nio.charset.StandardCharsets; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.jupiter.api.Assertions.*; +import static org.linguafranca.pwdb.format.KdbxSerializer.createUnencryptedInputStream; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** @@ -45,6 +44,7 @@ public class KdbxKeyFileTest { private static void toConsole(InputStream is) throws IOException { printStream.println(CharStreams.toString(new InputStreamReader(is, StandardCharsets.UTF_8))); } + /** * Test that we can load a key file and get a 32 byte base64 encoded value back */ @@ -63,12 +63,12 @@ public void testLoad() throws Exception { public void testEmptyPasswordCreds() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPassword.kdbx"); Credentials credentials = new KdbxCredentials(new byte[0]); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } /** - Test for empty password with key file + * Test for empty password with key file */ @Test public void testEmptyPasswordKeyCreds() throws Exception { @@ -76,12 +76,12 @@ public void testEmptyPasswordKeyCreds() throws Exception { InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("EmptyPasswordWithKey.key"); assert inputStreamKeyFile != null; Credentials credentials = new KdbxCredentials(new byte[0], inputStreamKeyFile); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } /** - Test for no master password with key + * Test for no master password with key */ @Test public void testNoPasswordKeyCreds() throws Exception { @@ -89,18 +89,18 @@ public void testNoPasswordKeyCreds() throws Exception { InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("NoPasswordWithKey.key"); assert inputStreamKeyFile != null; Credentials credentials = new KdbxCredentials(inputStreamKeyFile); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } /** - Test for empty password + * Test for empty password */ @Test public void testEmptyPassword() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPassword.kdbx"); Credentials credentials = new KdbxCredentials(new byte[0]); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -112,20 +112,23 @@ public void testSignedKeyFile() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.keyx"); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } - /** + /** * Test hash fails in KeyFile (v2.0) */ - @Test(expected = RuntimeException.class) + @Test public void testSignatureFails() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.kdbx"); - InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test_wrong_hash.keyx"); - Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); - toConsole(decryptedInputStream); + InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test_wrong_hash" + + ".keyx"); + Throwable throwable = assertThrows(RuntimeException.class, () -> { + Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + toConsole(decryptedInputStream); + }); } /** @@ -136,7 +139,7 @@ public void testKeyFileRandom() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdb_with_random_file.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("random_file"); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -148,7 +151,7 @@ public void testKeyFileHex64() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_keyfile64.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile64"); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -160,7 +163,7 @@ public void testKeyFile32() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_keyfile32.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile32"); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); - InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); + InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } } diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java index fde4ced3..6d3f6679 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java @@ -18,7 +18,7 @@ package org.linguafranca.pwdb.kdbx; import com.google.common.io.CharStreams; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; @@ -28,7 +28,7 @@ import java.nio.charset.StandardCharsets; import java.util.Scanner; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java index 4cca6eb8..f7a455c4 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/security/StreamEncryptionTest.java @@ -17,13 +17,13 @@ package org.linguafranca.pwdb.security; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.security.StreamEncryptor.*; import java.io.PrintStream; import java.security.SecureRandom; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** diff --git a/kdbx-database/src/test/resources/kdbx_hash_test_wrong_hash.keyx b/kdbx-io/src/test/resources/kdbx_hash_test_wrong_hash.keyx similarity index 100% rename from kdbx-database/src/test/resources/kdbx_hash_test_wrong_hash.keyx rename to kdbx-io/src/test/resources/kdbx_hash_test_wrong_hash.keyx diff --git a/pom.xml b/pom.xml index edadd0e4..0e10485f 100644 --- a/pom.xml +++ b/pom.xml @@ -211,11 +211,6 @@ junit-jupiter-engine test - - org.junit.vintage - junit-vintage-engine - test - org.slf4j slf4j-api From b41301d840e6249cdfadea47444f8dd03e8c08e6 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Thu, 13 Mar 2025 17:29:10 +0000 Subject: [PATCH 46/66] further major refactoring around test structure --- .../pwdb/basic/BasicDatabaseSerializer.java | 21 ++++ .../pwdb/basic/BasicDatabaseTest.java | 71 +++--------- .../java/org/linguafranca/pwdb/Database.java | 10 ++ .../pwdb/abstractdb/AbstractDatabase.java | 18 +++ .../pwdb/test/DatabaseTestBase.java | 108 ++++++++++++++++++ .../linguafranca/pwdb/test/Test123Test.java | 19 +-- .../linguafranca/pwdb/kdb/KdbDatabase.java | 8 ++ .../org/linguafranca/pwdb/kdb/Kdb123Test.java | 51 --------- .../pwdb/kdb/KdbDatabaseTest.java | 59 +++++++--- .../pwdb/kdb/KdbSerializerTest.java | 42 ------- .../linguafranca/pwdb/kdb/KeyFileTest.java | 41 ------- .../pwdb/kdbx/jackson/KdbxDatabase.java | 11 ++ .../pwdb/kdbx/database/Kdbx123Test.java | 24 ++-- .../pwdb/kdbx/database/KdbxBinariesTest.java | 44 +++++++ .../kdbx/database/KdbxBinariesV3Test.java | 90 --------------- .../pwdb/kdbx/database/KdbxDatabaseTest.java | 74 ++---------- .../kdbx/database/KdbxSaveAndReloadTest.java | 64 +---------- ...xBinariesV4Test.java => KdbxTestBase.java} | 13 ++- .../kdbx/database/validation/Issue27Test.java | 2 +- .../kdbx/database/validation/Issue33Test.java | 2 +- .../kdbx/database/validation/Issue70Test.java | 2 +- .../linguafranca/pwdb/format/KdbxCreds.java | 2 +- 22 files changed, 328 insertions(+), 448 deletions(-) create mode 100644 database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java delete mode 100644 kdb/src/test/java/org/linguafranca/pwdb/kdb/Kdb123Test.java delete mode 100644 kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java delete mode 100644 kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java delete mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java rename kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/{KdbxBinariesV4Test.java => KdbxTestBase.java} (62%) diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java index a671b730..5d73882f 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java @@ -56,6 +56,27 @@ public interface BasicDatabaseSerializer { */ BasicDatabase load(InputStream inputStream) throws IOException; + /** + * Serialize a BasicDatabase to a stream, with a runtime exception thrown if an IOException occurs + */ + default void saveNx(BasicDatabase database, OutputStream outputStream){ + try { + save(database, outputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + * Deserialize a BasicDatabase from a stream, with a runtime exception thrown if an IOException occurs + */ + default BasicDatabase loadNx(InputStream inputStream){ + try { + return load(inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } /** * XML implementation of containing BasicDatabaseSerializer */ diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java index c7e7924a..f0d07c09 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java @@ -17,61 +17,24 @@ package org.linguafranca.pwdb.basic; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.test.*; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; - -public class BasicDatabaseTest implements - TrivialDatabaseTest, - GroupsAndEntriesTest, - RecycleBinTest, - ProtectedPropertyTest, - ProtectedPropertyTest2 { - - Database database; - - /** - * Create a new database - */ - @Override - public Database createDatabase() { - return new BasicDatabase(); - } - - /** - * Create a new database for default use in tests - */ - @Override - public void newDatabase() { - database = createDatabase(); - } - - /** - * Get the current default database - */ - @Override - public Database getDatabase() { - return database; +public class BasicDatabaseTest + extends + DatabaseTestBase + implements + TrivialDatabaseTest, + GroupsAndEntriesTest, + RecycleBinTest, + ProtectedPropertyTest, + ProtectedPropertyTest2 { + + BasicDatabaseTest() { +super(BasicDatabase::new, + (credentials, inputStream) -> + new BasicDatabaseSerializer.Xml().loadNx(inputStream), + (database, credentials, outputStream) -> + new BasicDatabaseSerializer.Xml().saveNx((BasicDatabase) database, outputStream), + (credentials) -> null); } - - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException { - new BasicDatabaseSerializer.Xml().save((BasicDatabase) database, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ - return new BasicDatabaseSerializer.Xml().load(inputStream); - } - - @Override - public Credentials getCredentials(byte[] credentials){ - return new Credentials.None(); - } - } diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index 421b24d5..e11c6bc9 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -237,11 +237,21 @@ public interface Database { */ void save(Credentials credentials, OutputStream outputStream) throws IOException; + /** + * Save the database to a stream using default format - avoiding checked exception + */ + void saveNx(Credentials credentials, OutputStream outputStream); + /** * Save the database to a stream and closes the stream */ void save(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) throws IOException; + /** + * Save the database to a stream and closes the stream - avoiding checked exception + */ + void saveNx(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream); + /** * Get the format the database was loaded from */ diff --git a/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java index 1ebb319a..03ffb624 100644 --- a/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/AbstractDatabase.java @@ -19,6 +19,7 @@ import org.linguafranca.pwdb.*; +import java.io.OutputStream; import java.util.ArrayList; import java.util.List; import java.util.UUID; @@ -263,4 +264,21 @@ public boolean supportsPropertyValueStrategy(){ return false; } + @Override + public void saveNx(Credentials credentials, OutputStream outputStream) { + try { + save(credentials, outputStream); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + @Override + public void saveNx(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) { + try { + save(streamFormat, credentials, outputStream); + } catch (Exception e) { + throw new RuntimeException(e); + } + } } diff --git a/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java b/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java new file mode 100644 index 00000000..8f09a16f --- /dev/null +++ b/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.linguafranca.pwdb.test; + +import org.junit.jupiter.api.BeforeAll; +import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.Database; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Objects; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; + +public class DatabaseTestBase { + + @FunctionalInterface + public interface TriConsumer { + + void accept(A a, B b, C c); + + default TriConsumer andThen(TriConsumer after) { + Objects.requireNonNull(after); + + return (l, r,c) -> { + accept(l, r, c); + after.accept(l, r, c); + }; + } + } + + static String OUTPUT_DIRECTORY_PATH = "testOutput"; + + @BeforeAll + static void baseBeforeAll() throws Exception { + Files.createDirectories(Paths.get(OUTPUT_DIRECTORY_PATH)); + } + + protected Database database; + + Supplier creator; + BiFunction loader; + TriConsumer saver; + Function credentials; + + public DatabaseTestBase(Supplier creator, + BiFunction loader, + TriConsumer saver, + Function credentials) { + this.creator = creator; + this.loader = loader; + this.saver = saver; + this.credentials = credentials; + } + /** + * Create a new database + */ + public Database createDatabase() { + return this.creator.get(); + } + + /** + * Create a new database for default use in tests + */ + public void newDatabase() { + database = createDatabase(); + } + + /** + * Get the current default database + */ + public Database getDatabase() { + return database; + } + + public Database loadDatabase(byte[] credentials, String resourceName) { + return loader.apply(getCredentials(credentials), getClass().getClassLoader().getResourceAsStream(resourceName)); + } + + public Database loadDatabase(Credentials credentials, InputStream inputStream) { + return loader.apply(credentials, inputStream); + } + + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream){ + saver.accept(database, credentials, outputStream); + } + + public Credentials getCredentials(byte[] credentials){ + return this.credentials.apply(credentials); + } +} diff --git a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java index 21391c69..957d91eb 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java @@ -18,6 +18,7 @@ package org.linguafranca.pwdb.test; import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Visitor; @@ -42,22 +43,26 @@ public interface Test123Test { boolean getSkipDateCheck(); - Database getDatabase(); + Database loadDatabase(Credentials credentials, java.io.InputStream inputStream); + Credentials getCredentials(byte[] credentials); + String getFileName(); + SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssX"); @Test default void test123File() throws ParseException { - + Database database = loadDatabase(getCredentials("123".getBytes()), + getClass().getClassLoader().getResourceAsStream(getFileName())); // visit all groups and entries and list them to console - getDatabase().visit(new Visitor.Print(getTestPrintStream())); + database.visit(new Visitor.Print(getTestPrintStream())); // find all entries in the database // the kdb version has three additional system related entries - List anything = getDatabase().findEntries(""); + List anything = database.findEntries(""); assertTrue(10 <= anything.size()); // find all entries in the database that have the string "test" in them - List tests = getDatabase().findEntries("test"); + List tests = database.findEntries("test"); for (Entry tes: tests) { getTestPrintStream().println(tes.getTitle()); } @@ -71,14 +76,14 @@ default void test123File() throws ParseException { assertEquals("123", pass2); } - List passwords = getDatabase().findEntries("password"); + List passwords = database.findEntries("password"); assertEquals(4, passwords.size()); for (Entry passwordEntry : passwords) { assertEquals(passwordEntry.getTitle(), passwordEntry.getProperty(PASSWORD)); getTestPrintStream().println(passwordEntry.getTitle()); } - List entries = getDatabase().findEntries(entry -> entry.getTitle().equals("hello world")); + List entries = database.findEntries(entry -> entry.getTitle().equals("hello world")); assertEquals(1, entries.size()); assertEquals("pass", entries.get(0).getProperty(PASSWORD)); diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java index dfc759e8..7529dbee 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbDatabase.java @@ -50,6 +50,14 @@ public static KdbDatabase load(Credentials credentials, InputStream inputStream) return KdbSerializer.createKdbDatabase(credentials, new KdbHeader(), inputStream); } + public static KdbDatabase loadNx(Credentials credentials, InputStream inputStream) { + try { + return KdbDatabase.load(credentials, inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * Primarily intended for finding the parent of an Entry, when deserializing KDB data. * diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/Kdb123Test.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/Kdb123Test.java deleted file mode 100644 index 426924d6..00000000 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/Kdb123Test.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.kdb; - -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.test.Test123Test; - -import java.io.IOException; -import java.io.InputStream; - -/** - * @author jo - */ -public class Kdb123Test implements Test123Test { - - KdbDatabase kdbDatabase; - - Kdb123Test() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); - // file has password credentials - Credentials credentials = new KdbCredentials.Password("123".getBytes()); - // open database. - kdbDatabase = KdbDatabase.load(credentials, inputStream); - } - - @Override - public Database getDatabase() { - return kdbDatabase; - } - - @Override - public boolean getSkipDateCheck() { - return true; - } -} diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java index c8ad7556..2fdaf772 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java @@ -18,39 +18,66 @@ package org.linguafranca.pwdb.kdb; import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.Visitor; +import org.linguafranca.pwdb.test.DatabaseTestBase; import org.linguafranca.pwdb.test.GroupsAndEntriesTest; import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.test.Test123Test; + +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.linguafranca.util.TestUtil.getTestPrintStream; /** * @author Jo */ -public class KdbDatabaseTest implements GroupsAndEntriesTest { - - KdbDatabase kdbDatabase; +public class KdbDatabaseTest + extends + DatabaseTestBase + implements + GroupsAndEntriesTest, + Test123Test { - {newDatabase();} - - @Override - public KdbDatabase createDatabase() { - return new KdbDatabase(); + KdbDatabaseTest() { + super(KdbDatabase::new, KdbDatabase::loadNx, Database::saveNx, KdbCredentials.Password::new); + newDatabase(); } @Override - public void newDatabase() { - kdbDatabase = createDatabase(); + public boolean getSkipDateCheck() { + return true; } @Override - public Database getDatabase() { - return kdbDatabase; + public String getFileName(){ + return "test123.kdb"; } @Test public void supportedFunctionalityTest(){ - assertFalse(kdbDatabase.supportsBinaryProperties()); - assertFalse(kdbDatabase.supportsNonStandardPropertyNames()); - assertFalse(kdbDatabase.supportsRecycleBin()); - assertFalse(kdbDatabase.supportsPropertyValueStrategy()); + assertFalse(getDatabase().supportsBinaryProperties()); + assertFalse(getDatabase().supportsNonStandardPropertyNames()); + assertFalse(getDatabase().supportsRecycleBin()); + assertFalse(getDatabase().supportsPropertyValueStrategy()); + } + + @Test + public void testCreateKdbDatabase() throws Exception { + InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); + Database database = KdbDatabase.load(new KdbCredentials.Password("123".getBytes()), inputStream); + database.visit(new Visitor.Print(getTestPrintStream())); + } + + @Test + public void openKdbWithKeyFile() throws IOException { + InputStream key = getClass().getClassLoader().getResourceAsStream("kdb.key"); + KdbCredentials creds = new KdbCredentials.KeyFile("123".getBytes(), key); + InputStream is = getClass().getClassLoader().getResourceAsStream("kdbwithkey.kdb"); + KdbDatabase db = KdbDatabase.load(creds, is); + assertEquals(1, db.getRootGroup().getGroupsCount()); + assertEquals("General", db.getRootGroup().getGroups().get(0).getName()); } } diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java deleted file mode 100644 index d040e1e8..00000000 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbSerializerTest.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.kdb; - -import org.junit.jupiter.api.Test; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.Visitor; - -import java.io.InputStream; -import java.io.PrintStream; - -import static org.linguafranca.util.TestUtil.getTestPrintStream; - -/** - * @author jo - */ -public class KdbSerializerTest { - - static PrintStream printStream = getTestPrintStream(); - - @Test - public void testCreateKdbDatabase() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); - Database database = KdbDatabase.load(new KdbCredentials.Password("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); - } -} \ No newline at end of file diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java deleted file mode 100644 index ec7a4f5e..00000000 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KeyFileTest.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.kdb; - -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.io.InputStream; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * @author jo - */ -public class KeyFileTest { - - @Test - public void openKdbWithKeyFile() throws IOException { - InputStream key = getClass().getClassLoader().getResourceAsStream("kdb.key"); - KdbCredentials creds = new KdbCredentials.KeyFile("123".getBytes(), key); - InputStream is = getClass().getClassLoader().getResourceAsStream("kdbwithkey.kdb"); - KdbDatabase db = KdbDatabase.load(creds, is); - assertEquals(1, db.getRootGroup().getGroupsCount()); - assertEquals("General", db.getRootGroup().getGroups().get(0).getName()); - } -} \ No newline at end of file diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index dbc6b886..4f2e8697 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -83,6 +83,17 @@ public static KdbxDatabase load(Credentials credentials, InputStream inputStream return new KdbxDatabase(jsd.keePassFile, streamFormat); } + /** + * Load kdbx file - avoiding checked exceptions + */ + public static KdbxDatabase loadNx(Credentials credentials, InputStream inputStream) { + try { + return load(credentials, inputStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + /** * Save the database with the same stream format that it was loaded with, or V4 * default if none diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java index 8d8f650f..ff0a7c90 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java @@ -29,25 +29,17 @@ /** * @author jo */ -public class Kdbx123Test implements Test123Test { - - KdbxDatabase kdbxDatabase; - - Kdbx123Test() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - // file has password credentials - Credentials credentials = new KdbxCredentials("123".getBytes()); - // open database. - kdbxDatabase = KdbxDatabase.load(credentials, inputStream); - } +public class Kdbx123Test + extends KdbxTestBase + implements Test123Test { @Override - public Database getDatabase() { - return kdbxDatabase; + public boolean getSkipDateCheck() { + return false; } @Override - public boolean getSkipDateCheck() { - return false; + public String getFileName() { + return "test123.kdbx"; } -} +} \ No newline at end of file diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java new file mode 100644 index 00000000..62601b5d --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.linguafranca.pwdb.kdbx.database; + +import org.junit.jupiter.api.Nested; +import org.linguafranca.pwdb.test.BinaryPropertiesTest; + +public class KdbxBinariesTest { + + @Nested + public class KdbxBinariesV4Test + extends + KdbxTestBase + implements + BinaryPropertiesTest { + KdbxBinariesV4Test() { + database = loadDatabase("123".getBytes(), "V4-ChaCha20-Argon2-Attachment.kdbx"); + } + } + @Nested + public class KdbxBinariesV3Test + extends + KdbxTestBase + implements + BinaryPropertiesTest { + KdbxBinariesV3Test() { + database = loadDatabase("123".getBytes(), "Attachment.kdbx"); + } + } +} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java deleted file mode 100644 index f7f39c4e..00000000 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV3Test.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.linguafranca.pwdb.kdbx.database; - -import org.junit.jupiter.api.BeforeAll; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.format.KdbxCredentials; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; -import org.linguafranca.pwdb.test.*; - -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Paths; - -public class KdbxBinariesV3Test implements - BinaryPropertiesTest { - - static String OUTPUT_DIRECTORY_PATH = "testOutput"; - public String databaseName = "Attachment.kdbx"; - - @BeforeAll - static void ppt2BeforeAll() throws Exception { - Files.createDirectories(Paths.get(OUTPUT_DIRECTORY_PATH)); - } - - Database database; - { - newDatabase(); - } - /** - * Create a new database - */ - @Override - public Database createDatabase() { - return new KdbxDatabase(); - } - - /** - * Create a new database for default use in tests - */ - @Override - public void newDatabase() { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream(databaseName); - try { - database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()),inputStream); - } catch (Exception e) { - throw new IllegalStateException(e); - } - } - - /** - * Get the current default database - */ - @Override - public Database getDatabase() { - return database; - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException{ - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ - return KdbxDatabase.load(credentials, inputStream); - } - - @Override - public Credentials getCredentials(byte[] credentials){ - return new KdbxCredentials(credentials); - } -} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java index 94cdad4b..d2781a5f 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java @@ -16,70 +16,14 @@ */ package org.linguafranca.pwdb.kdbx.database; -import org.junit.jupiter.api.BeforeAll; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.format.KdbxCredentials; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.test.*; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Paths; - -public class KdbxDatabaseTest implements - TrivialDatabaseTest, - GroupsAndEntriesTest, - RecycleBinTest, - ProtectedPropertyTest, - ProtectedPropertyTest2 { - - static String OUTPUT_DIRECTORY_PATH = "testOutput"; - @BeforeAll - static void ppt2BeforeAll() throws Exception { - Files.createDirectories(Paths.get(OUTPUT_DIRECTORY_PATH)); - } - - Database database; - - /** - * Create a new database - */ - @Override - public Database createDatabase() { - return new KdbxDatabase(); - } - - /** - * Create a new database for default use in tests - */ - @Override - public void newDatabase() { - database = createDatabase(); - } - - /** - * Get the current default database - */ - @Override - public Database getDatabase() { - return database; - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException{ - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ - return KdbxDatabase.load(credentials, inputStream); - } - - @Override - public Credentials getCredentials(byte[] credentials){ - return new KdbxCredentials(credentials); - } -} +public class KdbxDatabaseTest + extends + KdbxTestBase + implements + TrivialDatabaseTest, + GroupsAndEntriesTest, + RecycleBinTest, + ProtectedPropertyTest, + ProtectedPropertyTest2 { } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java index 366dd7c8..9af60a1b 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java @@ -16,73 +16,17 @@ */ package org.linguafranca.pwdb.kdbx.database; -import org.junit.jupiter.api.BeforeAll; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.StreamFormat; -import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; -import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.test.*; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.file.Files; -import java.nio.file.Paths; +public class KdbxSaveAndReloadTest + extends KdbxTestBase + implements KdbxFileSaveAndReloadTest { -public class KdbxSaveAndReloadTest implements - KdbxFileSaveAndReloadTest { - - static String OUTPUT_DIRECTORY_PATH = "testOutput"; - @BeforeAll - static void ppt2BeforeAll() throws Exception { - Files.createDirectories(Paths.get(OUTPUT_DIRECTORY_PATH)); - } - - Database database; - - /** - * Create a new database - */ - @Override - public Database createDatabase() { - return new KdbxDatabase(); - } - - /** - * Create a new database for default use in tests - */ - @Override - public void newDatabase() { - database = createDatabase(); - } - - /** - * Get the current default database - */ - @Override - public Database getDatabase() { - return database; - } - - @Override - public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException{ - database.save(credentials, outputStream); - } - - @Override - public Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException{ - return KdbxDatabase.load(credentials, inputStream); - } - - @Override - public Credentials getCredentials(byte[] credentials){ - return new KdbxCredentials(credentials); - } @Override - public boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2) { + public boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2) { KdbxHeader h1 = (KdbxHeader) s1.getStreamConfiguration(); KdbxHeader h2 = (KdbxHeader) s1.getStreamConfiguration(); return (h1.getVersion() == h2.getVersion() && diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV4Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxTestBase.java similarity index 62% rename from kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV4Test.java rename to kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxTestBase.java index 0cc5c347..ac350067 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesV4Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxTestBase.java @@ -17,7 +17,16 @@ package org.linguafranca.pwdb.kdbx.database; -public class KdbxBinariesV4Test extends KdbxBinariesV3Test { - String databaseName = "V4-ChaCha20-Argon2-Attachment.kdbx"; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import org.linguafranca.pwdb.test.*; +public abstract class KdbxTestBase + extends + DatabaseTestBase { + + KdbxTestBase() { + super(KdbxDatabase::new, KdbxDatabase::loadNx, Database::saveNx, KdbxCredentials::new); + } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java index 2a8b0589..6b2ef3d0 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java @@ -71,7 +71,7 @@ public void testV4Date() throws IOException { } } -@Disabled + @Test public void testIssue27XML() throws IOException { InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java index 0659ec94..14b9981b 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java @@ -34,7 +34,7 @@ import java.nio.file.Paths; /** - * Review Issue-33 https://github.com/jorabin/KeePassJava2/issues/33 + * Review Issue-33 */ public class Issue33Test { diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java index 7ff77b44..d46f3300 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java @@ -25,7 +25,7 @@ import java.io.InputStream; /** - * Review Issue-70 https://github.com/jorabin/KeePassJava2/issues/70 + * Review Issue-70 */ public class Issue70Test { diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java index 4d218c28..7f8f5020 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java @@ -22,7 +22,7 @@ import java.io.InputStream; /** - * Convenience wrapper to convert to KdbxCredentials - for helping with code migration to version 3. + * Convenience wrapper to convert to KdbxCredentials - for helping with code migration to KeePassJava2 version 3. * * @deprecated use {@link KdbxCredentials} instead */ From a382ec267b837fddace510207b4f8a12aaf6d0c0 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Thu, 13 Mar 2025 21:08:03 +0000 Subject: [PATCH 47/66] adding exploratory encrypted output to basic database --- basic/pom.xml | 6 ++ .../pwdb/basic/BasicDatabase.java | 28 ++++++--- .../pwdb/basic/BasicDatabaseSerializer.java | 60 ++++++++++++++++--- .../basic/BasicDatabaseSerializerTest.java | 11 +--- 4 files changed, 81 insertions(+), 24 deletions(-) diff --git a/basic/pom.xml b/basic/pom.xml index 982b9afd..9e5e0572 100644 --- a/basic/pom.xml +++ b/basic/pom.xml @@ -57,6 +57,12 @@ ${project.version} test-jar + + org.linguafranca.pwdb + KeePassJava2.kdbx.io + 3.0.0-SNAPSHOT + compile + \ No newline at end of file diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index f04cde9e..318ca54a 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -19,20 +19,25 @@ import org.jetbrains.annotations.Nullable; import org.linguafranca.pwdb.*; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; +import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.protect.ProtectedDatabase; import java.io.IOException; import java.io.OutputStream; import java.time.Instant; +import java.util.Objects; import java.util.Stack; /** - * A basic implementation of a Database + * A basic implementation of a Database for experimentation and testing */ public class BasicDatabase extends ProtectedDatabase { private final BasicGroup root = new BasicGroup(this, "Root"); private String databaseName = "New Database"; private String databaseDescription = "Database created on " + Instant.now().toString(); + private StreamFormat streamFormat; @Override public Group getRootGroup() { @@ -96,24 +101,33 @@ public void setDescription(String description) { @Override public void save(Credentials credentials, OutputStream outputStream) throws IOException { - throw new UnsupportedOperationException(); + if (Objects.isNull(streamFormat)) { + streamFormat = new KdbxStreamFormat(new KdbxHeader(4)); + } + save(streamFormat, credentials, outputStream); } @Override - public void save(StreamFormat streamFormat, Credentials credentials, OutputStream outputStream) throws IOException { - throw new UnsupportedOperationException(); + public void save(StreamFormat streamFormat, + Credentials credentials, + OutputStream outputStream) throws IOException { + KdbxStreamFormat kdbxStreamFormat = (KdbxStreamFormat) streamFormat; + KdbxHeader header = kdbxStreamFormat.getStreamConfiguration(); + OutputStream encryptedOutputStream = KdbxSerializer.createEncryptedOutputStream(credentials, header, outputStream); + BasicDatabaseSerializer bds = new BasicDatabaseSerializer.Xml(header.getStreamEncryptor()); + bds.save(this, encryptedOutputStream); } @Override - public StreamFormat getStreamFormat() { - throw new UnsupportedOperationException(); + public StreamFormat getStreamFormat() { + return streamFormat; } /** * A visitor that can be used to fix up the parent and database fields of groups and entries * after deserialization of the database */ - public static class FixupVisitor implements Visitor{ + public static class FixupVisitor implements Visitor { final Stack stack = new Stack<>(); BasicDatabase database; diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java index 5d73882f..6508ca2a 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java @@ -28,7 +28,9 @@ import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.linguafranca.pwdb.PropertyValue; +import org.linguafranca.pwdb.StreamFormat; import org.linguafranca.pwdb.protect.ProtectedDatabase; +import org.linguafranca.pwdb.security.StreamEncryptor; import java.io.IOException; import java.io.InputStream; @@ -81,7 +83,17 @@ default BasicDatabase loadNx(InputStream inputStream){ * XML implementation of containing BasicDatabaseSerializer */ class Xml implements BasicDatabaseSerializer { - private final ObjectMapper objectMapper = init(); + private final ObjectMapper objectMapper; + private final StreamEncryptor streamEncryptor; + + public Xml() { + this(new StreamEncryptor.None()); + } + + public Xml(StreamEncryptor streamEncryptor) { + this.streamEncryptor = streamEncryptor; + this.objectMapper = init(); + } @Override public void save(BasicDatabase database, OutputStream outputStream) throws IOException { @@ -97,8 +109,8 @@ public BasicDatabase load(InputStream inputStream) throws IOException { private ObjectMapper init() { SimpleModule module = new SimpleModule() - .addDeserializer(PropertyValue.class, new PropertyValueDeserializer()) - .addSerializer(PropertyValue.class, new PropertyValueSerializer()) + .addDeserializer(PropertyValue.class, new PropertyValueDeserializer(streamEncryptor)) + .addSerializer(PropertyValue.class, new PropertyValueSerializer(streamEncryptor)) .addDeserializer(BasicIcon.class, new IconDeserializer()) .addSerializer(BasicIcon.class, new IconSerializer()); XmlMapper mapper = XmlMapper.builder() @@ -135,6 +147,16 @@ private ObjectMapper init() { * Configure special treatment on serialization of properties */ public static class PropertyValueSerializer extends JsonSerializer { + StreamEncryptor streamEncryptor; + + public PropertyValueSerializer() { + this(new StreamEncryptor.None()); + } + + public PropertyValueSerializer(StreamEncryptor streamEncryptor) { + this.streamEncryptor = streamEncryptor; + } + /** * Don't serialize empty properties *

@@ -163,18 +185,22 @@ public void serialize(PropertyValue value, JsonGenerator gen, SerializerProvider if (value.isProtected()) { xmlGenerator.setNextIsAttribute(true); xmlGenerator.writeStringField("protected", "true"); - asBinary = true; // write password as binary TODO later we can stream encode it + asBinary = true; // write password as binary } if (asBinary) { xmlGenerator.setNextIsAttribute(true); xmlGenerator.writeStringField("binary", "true"); } - xmlGenerator.setNextIsAttribute(false); xmlGenerator.setNextIsUnwrapped(true); + if (asBinary) { - xmlGenerator.writeBinaryField("text", value.getValueAsBytes()); + byte[] bytes = value.getValueAsBytes(); + if (value.isProtected()) { + bytes = streamEncryptor.encrypt(bytes); + } + xmlGenerator.writeBinaryField("text", bytes); } else { xmlGenerator.writeStringField("text", value.getValueAsString()); } @@ -186,7 +212,17 @@ public void serialize(PropertyValue value, JsonGenerator gen, SerializerProvider * Property deserialization */ public static class PropertyValueDeserializer extends JsonDeserializer { - PropertyValue.Strategy strategy = new PropertyValue.Strategy.Default(); + PropertyValue.Strategy strategy; + StreamEncryptor streamEncryptor; + + public PropertyValueDeserializer() { + this(new StreamEncryptor.None()); + } + + public PropertyValueDeserializer(StreamEncryptor streamEncryptor) { + this.streamEncryptor = streamEncryptor; + this.strategy = new PropertyValue.Strategy.Default(); + } @Override public PropertyValue deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { @@ -195,8 +231,12 @@ public PropertyValue deserialize(JsonParser jsonParser, DeserializationContext d if (node.isTextual()) { return strategy.newUnprotected().of(node.textValue()); } - if (node.has("protected") || node.has("binary")) { - return strategy.newProtected().of(node.get("").binaryValue()); + if (node.has("binary")) { + if (node.has("protected")) { + byte[] bytes = streamEncryptor.decrypt(node.get("").binaryValue()); + return strategy.newProtected().of(bytes); + } + return strategy.newUnprotected().of(node.get("").binaryValue()); } throw new IllegalStateException("Unknown property value format " + node); } @@ -265,6 +305,8 @@ protected static class BasicGroup {} ProtectedDatabase valueStrategy; @JsonIgnore boolean isDirty; + @JsonIgnore + StreamFormat streamFormat; } } } diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java index 19ee51af..5c3ddb4a 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java @@ -22,6 +22,7 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Visitor; +import org.linguafranca.pwdb.security.StreamEncryptor; import java.io.*; import java.nio.file.Files; @@ -87,14 +88,8 @@ public boolean isEntriesFirst() { @Test void saveAndLoad() throws IOException { - new BasicDatabaseSerializer.Xml().save(database, new FileOutputStream(TEST_OUTPUT_DIR + "test.xml")); - BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml().load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); + new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).save(database, new FileOutputStream(TEST_OUTPUT_DIR + "test.xml")); + BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); loadedDatabase.visit(visitor); } - - @Test - void load() throws IOException { - BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml().load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); - loadedDatabase.visit(new Visitor.Print()); - } } \ No newline at end of file From 76299d47d04a5220dbb48890ec435ccb931f2ae7 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Fri, 14 Mar 2025 13:36:33 +0000 Subject: [PATCH 48/66] Add a recursive fixup to basic and add getDatabase to Entry and modify return types --- .../pwdb/basic/BasicDatabase.java | 47 ++++++------------- .../pwdb/basic/BasicDatabaseSerializer.java | 2 +- .../linguafranca/pwdb/basic/BasicEntry.java | 28 ++++++----- .../linguafranca/pwdb/basic/BasicGroup.java | 16 +++---- .../basic/BasicDatabaseSerializerTest.java | 9 +++- .../java/org/linguafranca/pwdb/Entry.java | 6 +++ .../org/linguafranca/pwdb/kdb/KdbEntry.java | 6 +++ .../linguafranca/pwdb/kdb/KdbSerializer.java | 1 + .../pwdb/kdbx/jackson/KdbxDatabase.java | 23 ++++++++- .../pwdb/kdbx/jackson/KdbxEntry.java | 5 ++ .../jackson/KdbxSerializableDatabase.java | 18 ------- 11 files changed, 87 insertions(+), 74 deletions(-) diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 318ca54a..955dc47c 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -123,42 +123,25 @@ public StreamFormat getStreamFormat() { return streamFormat; } + /** - * A visitor that can be used to fix up the parent and database fields of groups and entries - * after deserialization of the database + * On load add parents + * + * @param parent a parent to recurse */ - public static class FixupVisitor implements Visitor { - final Stack stack = new Stack<>(); - BasicDatabase database; - - public FixupVisitor(BasicDatabase database) { - this.database = database; - } - - @Override - public void startVisit(Group group) { - ((BasicGroup) group).database = database; - if (!stack.isEmpty()) { - ((BasicGroup) group).parent = (BasicGroup) stack.peek(); - } - stack.push(group); - } - - @Override - public void endVisit(Group group) { - stack.pop(); - } - - @Override - public void visit(Entry entry) { - ((BasicEntry) entry).database = database; - ((BasicEntry) entry).parent = stack.peek(); + void fixUp(BasicGroup parent) { + parent.database = this; + for (Group group : parent.getGroups()) { + BasicGroup basicGroup = (BasicGroup) group; + basicGroup.parent = parent; + fixUp(basicGroup); } - @Override - public boolean isEntriesFirst() { - return false; + for (Entry entry : parent.getEntries()) { + BasicEntry basicEntry = (BasicEntry) entry; + basicEntry.database = this; + basicEntry.parent = parent; } - } + } diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java index 6508ca2a..c44425f1 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializer.java @@ -103,7 +103,7 @@ public void save(BasicDatabase database, OutputStream outputStream) throws IOExc @Override public BasicDatabase load(InputStream inputStream) throws IOException { BasicDatabase database2 = objectMapper.readValue(inputStream, BasicDatabase.class); - database2.visit(new BasicDatabase.FixupVisitor(database2)); + database2.fixUp((BasicGroup) database2.getRootGroup()); return database2; } diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java index a0189391..fa9653b8 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java @@ -19,18 +19,15 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Group; -import org.linguafranca.pwdb.Icon; -import org.linguafranca.pwdb.PropertyValue; +import org.linguafranca.pwdb.*; import org.linguafranca.pwdb.abstractdb.AbstractEntry; import java.time.Instant; import java.util.*; public class BasicEntry extends AbstractEntry { - BasicDatabase database; - protected Group parent; + protected BasicDatabase database; + protected BasicGroup parent; private final UUID uuid; private BasicIcon icon; @@ -76,7 +73,7 @@ public String getProperty(String name) { } @Override - public Entry setProperty(String name, String value) { + public BasicEntry setProperty(String name, String value) { updateModifiedTime(); properties.put(name, PropertyValue.StringStore.getFactory().of(value)); return this; @@ -109,21 +106,21 @@ public PropertyValue getPropertyValue(String name) { } @Override - public Entry setPropertyValue(String name, PropertyValue value) { + public BasicEntry setPropertyValue(String name, PropertyValue value) { properties.put(name, value); return this; } - public Entry addProperty(String name, byte[] value){ + public BasicEntry addProperty(String name, byte[] value){ properties.put(name, database.getPropertyValueStrategy().getFactoryFor(name).of(value)); return this; } - public Entry addProperty(String name, char[] value) { + public BasicEntry addProperty(String name, char[] value) { properties.put(name, database.getPropertyValueStrategy().getFactoryFor(name).of(value)); return this; } - public Entry addProperty(String name, CharSequence value){ + public BasicEntry addProperty(String name, CharSequence value){ properties.put(name, database.getPropertyValueStrategy().getFactoryFor(name).of(value)); return this; } @@ -156,7 +153,12 @@ public List getBinaryPropertyNames() { } @Override - public @Nullable Group getParent() { + public BasicDatabase getDatabase() { + return database; + } + + @Override + public @Nullable BasicGroup getParent() { return parent; } @@ -166,7 +168,7 @@ public List getBinaryPropertyNames() { } @Override - public Icon getIcon() { + public BasicIcon getIcon() { return icon; } diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java index 4b173d9e..e1bdc41a 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java @@ -31,8 +31,8 @@ import java.util.UUID; public class BasicGroup extends AbstractGroup { - BasicGroup parent; - BasicDatabase database; + protected BasicGroup parent; + protected BasicDatabase database; private final UUID uuid; private String name; private BasicIcon icon; @@ -60,7 +60,7 @@ public class BasicGroup extends AbstractGroup { @Override public boolean isRootGroup() { - return false; + return database.getRootGroup().equals(this); } @Override @@ -69,7 +69,7 @@ public boolean isRecycleBin() { } @Override - public @Nullable Group getParent() { + public @Nullable BasicGroup getParent() { return parent; } @@ -115,19 +115,19 @@ public int getEntriesCount() { } @Override - public Entry addEntry(Entry entry) { + public BasicEntry addEntry(Entry entry) { BasicEntry basicEntry = (BasicEntry) entry; entries.add(basicEntry); basicEntry.parent = this; - return entry; + return basicEntry; } @Override - public Entry removeEntry(Entry entry) { + public BasicEntry removeEntry(Entry entry) { BasicEntry basicEntry = (BasicEntry) entry; entries.remove(basicEntry); (basicEntry).parent = null; - return entry; + return basicEntry; } @Override diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java index 5c3ddb4a..415c9e23 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java @@ -58,7 +58,11 @@ static void setUp() throws IOException { @Override public void startVisit(Group group) { System.out.println("Group: " + group.getName() + " " + group.getUuid()); - //assertNotNull(database.findGroup(group.getUuid())); + assertNotNull(database.findGroup(group.getUuid())); + assertNotNull(group.getDatabase()); + if (!group.isRootGroup()) { + assertNotNull(group.getParent()); + } } @Override @@ -78,6 +82,8 @@ public void visit(Entry entry) { Entry entry1 = database.findEntry(entry.getUuid()); assertNotNull(entry1, "Entry not found " + entry.getUuid()); assertEquals(entry.getPropertyNames().size(), entry1.getPropertyNames().size()); + assertNotNull(entry.getDatabase()); + assertNotNull(entry.getParent()); } @Override @@ -91,5 +97,6 @@ void saveAndLoad() throws IOException { new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).save(database, new FileOutputStream(TEST_OUTPUT_DIR + "test.xml")); BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); loadedDatabase.visit(visitor); + System.out.println(loadedDatabase.getRootGroup().getDatabase().getName()); } } \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 46c90fdb..0beb636e 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -282,6 +282,12 @@ interface Matcher { */ List getBinaryPropertyNames(); + /** + * Get the database this entry belongs to + * @return a database + */ + Database getDatabase(); + /** * Get the parent of this entry * @return a parent or null if this entry currently does not have a parent diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index 0e7b22eb..1d5e903d 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -34,6 +34,7 @@ * @author jo */ public class KdbEntry extends AbstractEntry { + public KdbDatabase database; KdbGroup parent; private UUID uuid = UUID.randomUUID(); private String title = ""; @@ -95,6 +96,11 @@ public KdbGroup getParent() { return parent; } + @Override + public KdbDatabase getDatabase() { + return database; + } + @Override public @NotNull UUID getUuid() { return uuid; diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java index 0883ad16..baf88861 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java @@ -226,6 +226,7 @@ private static KdbGroup deserializeGroup(KdbGroup lastGroup, DataInput dataInput private static void deserializeEntry(KdbDatabase database, DataInput dataInput) throws IOException { int fieldType; KdbEntry entry = new KdbEntry(); + entry.database = database; while ((fieldType = dataInput.readUnsignedShort()) != 0xFFFF) { switch (fieldType) { case 0x0000: diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java index 4f2e8697..0771e102 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxDatabase.java @@ -49,7 +49,7 @@ public KdbxDatabase(KeePassFile file, StreamFormat streamFormat) { keePassFile = file; keePassFile.root.group.database = this; this.streamFormat = streamFormat; - KdbxSerializableDatabase.fixUp(keePassFile.root.group); + fixUp(keePassFile.root.group); } catch (Exception e) { throw new IllegalStateException(e); } @@ -220,4 +220,25 @@ public StreamFormat getStreamFormat() { return streamFormat; } + + /** + * On load add parents + * + * @param parent a parent to recurse + */ + static void fixUp(KdbxGroup parent) { + + for (KdbxGroup group : parent.groups) { + group.parent = parent; + group.database = parent.database; + fixUp(group); + } + + for (KdbxEntry entry : parent.entries) { + entry.database = parent.database; + entry.parent = parent; + } + } + + } diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index 7cc94866..2da9aa53 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -315,6 +315,11 @@ public List getBinaryPropertyNames() { return result; } + @Override + public KdbxDatabase getDatabase() { + return database; + } + @Override public KdbxGroup getParent() { return parent; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index 68e6b786..b813c648 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -172,24 +172,6 @@ public int getBinaryCount() { return keePassFile.meta.binaries.size(); } - /** - * On load add parents - * - * @param parent a parent to recurse - */ - static void fixUp(KdbxGroup parent) { - - for (KdbxGroup group : parent.groups) { - group.parent = parent; - group.database = parent.database; - fixUp(group); - } - - for (KdbxEntry entry : parent.entries) { - entry.database = parent.database; - entry.parent = parent; - } - } @Override public StreamEncryptor getEncryption() { From 24d7eeae8444358959ea9c561745dd4795e1e204 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Sat, 15 Mar 2025 13:58:36 +0000 Subject: [PATCH 49/66] remove maven run listener --- database/pom.xml | 5 -- .../linguafranca/util/MavenRunListener.java | 72 ------------------- pom.xml | 7 -- 3 files changed, 84 deletions(-) delete mode 100644 database/src/test/java/org/linguafranca/util/MavenRunListener.java diff --git a/database/pom.xml b/database/pom.xml index 35cc4b37..fbb83d91 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -39,11 +39,6 @@ org.bouncycastle bcpkix-jdk18on - - org.junit.vintage - junit-vintage-engine - test - diff --git a/database/src/test/java/org/linguafranca/util/MavenRunListener.java b/database/src/test/java/org/linguafranca/util/MavenRunListener.java deleted file mode 100644 index dc5f9ba1..00000000 --- a/database/src/test/java/org/linguafranca/util/MavenRunListener.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.util; - -import org.junit.runner.Description; -import org.junit.runner.notification.Failure; -import org.junit.runner.notification.RunListener; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; - -/** - * Helper class to identify the test being run by maven surefire in the log output. This - * is particularly useful if there is a lot of "chatty" testing going on. - *

- * See: Using Custom Listeners and Reporters - */ -@RunListener.ThreadSafe -public class MavenRunListener extends RunListener { - static Logger logger = LoggerFactory.getLogger("MavenRunListener"); - static Marker marker = MarkerFactory.getMarker("Starting Test"); - static Marker endMarker = MarkerFactory.getMarker("Finished Test"); - static Marker failMarker = MarkerFactory.getMarker("Failed Test"); - static Marker assumeFailMarker = MarkerFactory.getMarker("Assumption Failed"); - static Marker ignoredMarker = MarkerFactory.getMarker("Ignored Test"); - - @Override - public void testStarted(Description description) throws Exception { - super.testStarted(description); - logger.info(marker, "{}", description.getDisplayName()); - } - - @Override - public void testFailure(Failure failure) throws Exception { - super.testFailure(failure); - logger.error(failMarker, "{} {}", failure.getMessage(), failure.getDescription().getDisplayName()); - } - - @Override - public void testAssumptionFailure(Failure failure) { - super.testAssumptionFailure(failure); - logger.warn(assumeFailMarker, "{} {}", failure.getMessage(), failure.getDescription().getDisplayName()); - } - - @Override - public void testIgnored(Description description) throws Exception { - super.testIgnored(description); - logger.info(ignoredMarker, "{}", description.getDisplayName()); - } - - @Override - public void testFinished(Description description) throws Exception { - super.testFinished(description); - logger.info(endMarker, "Finished {}", description.getDisplayName()); - } -} diff --git a/pom.xml b/pom.xml index 0e10485f..cc90baf2 100644 --- a/pom.xml +++ b/pom.xml @@ -104,13 +104,6 @@ ${maven.multiModuleProjectDirectory}/logback.xml - - - - listener - org.linguafranca.util.MavenRunListener - - From fe693eb7f9966870a10bca4236fe25f70eb3b586 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 17 Mar 2025 10:39:36 +0000 Subject: [PATCH 50/66] Introduce Test Execution listener in place of Maven Run Listener --- .../linguafranca/util/ExecutionListener.java | 60 +++++++++++++++++++ ...it.platform.launcher.TestExecutionListener | 17 ++++++ logback.xml | 2 +- 3 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 database/src/test/java/org/linguafranca/util/ExecutionListener.java create mode 100644 database/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener diff --git a/database/src/test/java/org/linguafranca/util/ExecutionListener.java b/database/src/test/java/org/linguafranca/util/ExecutionListener.java new file mode 100644 index 00000000..fcbebf54 --- /dev/null +++ b/database/src/test/java/org/linguafranca/util/ExecutionListener.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.util; + +import org.junit.platform.engine.TestExecutionResult; +import org.junit.platform.launcher.TestExecutionListener; +import org.junit.platform.launcher.TestIdentifier; +import org.slf4j.*; + +public class ExecutionListener implements TestExecutionListener { + + static Logger logger = LoggerFactory.getLogger("MavenRunListener"); + static Marker marker = MarkerFactory.getMarker("Starting"); + static Marker endMarker = MarkerFactory.getMarker("Finished"); + static Marker failMarker = MarkerFactory.getMarker("Failed"); + static Marker assumeFailMarker = MarkerFactory.getMarker("Assumption Failed"); + static Marker ignoredMarker = MarkerFactory.getMarker("Ignored"); + static Marker skippedMarker = MarkerFactory.getMarker("Skipped"); + + @Override + public void executionSkipped(TestIdentifier testIdentifier, String reason) { + logger.info(ignoredMarker, "{} {}", testIdentifier.getDisplayName(), reason); + } + + @Override + public void executionStarted(TestIdentifier testIdentifier) { + logger.info(marker, "{}", testIdentifier.getDisplayName()); + } + + @Override + public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult) { + MDC.put("result", testExecutionResult.getStatus().toString()); + switch (testExecutionResult.getStatus()) { + case FAILED: + logger.error(failMarker, "{}", testIdentifier.getDisplayName()); + break; + case ABORTED: + logger.warn(ignoredMarker, "{}", testIdentifier.getDisplayName()); + break; + case SUCCESSFUL: + logger.info(endMarker,"{}", testIdentifier.getDisplayName()); + } + MDC.clear(); + } +} diff --git a/database/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener b/database/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener new file mode 100644 index 00000000..c5b20de5 --- /dev/null +++ b/database/src/test/resources/META-INF/services/org.junit.platform.launcher.TestExecutionListener @@ -0,0 +1,17 @@ +# +# Copyright (c) 2025. Jo Rabin +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# +org.linguafranca.util.ExecutionListener \ No newline at end of file diff --git a/logback.xml b/logback.xml index 566fa576..30f917a1 100644 --- a/logback.xml +++ b/logback.xml @@ -24,7 +24,7 @@ when compiled/run the root maven POM - -%highlight(%-5level) %d{HH:mm:ss.SSS} [%thread] %logger{36} - %cyan(%marker) %msg%n + -%highlight(%-5level) %d{HH:mm:ss.SSS} [%thread] %logger{36} - %cyan(%-8marker) %msg %highlight(%X{result}) %n From b99258e177e8d842fb73717799329e98d3fd2a39 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 17 Mar 2025 10:42:04 +0000 Subject: [PATCH 51/66] update changelog --- CHANGELOG.md | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b0e8af5..8fb5b9b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,16 +2,36 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.com) and [Semantic Versioning](http://semver.org/spec/v2.0.0.html) -## 2024-11-22 [3.0.0-SNAPSHOT] +## [3.0.0-SNAPSHOT] (ongoing) ### Changed -- remove Simple database -- remove JAXB database -- remove DOM database -- remove complicated generics on database classes - -## 2025-01-05 [2.2.3] +- update to Java 11 +- refactor API + - remove complicated generics + - fluent element builder + - additional convenience methods +- refactor examples +- refactor modules + - rename Jackson database to KDBX database + - add experimental `basic` database support + - remove Simple database + - remove JAXB database + - remove DOM database + - remove `test` and `util` modules + - remove `all` module +- refactor tests + - restructure + - create a test-jar for dependencies + - "upgrade" to JUnit 5 + + +## [2.2.4] 2025-03-05 + +- [Issue 76, 78] Resolve incompatibility with KeePassXC (empty elements) +- [Issue 73] Trying to resolve dependency clashes for Woodstox etc + +## [2.2.3] 2025-01-05 ### Added From 5213b830bf7ba4d7389f6ff0173aae9477719c11 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 17 Mar 2025 11:52:08 +0000 Subject: [PATCH 52/66] reverse removal of `all` module --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fb5b9b5..aba7fdf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,7 +19,6 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.co - remove JAXB database - remove DOM database - remove `test` and `util` modules - - remove `all` module - refactor tests - restructure - create a test-jar for dependencies From b9c1974d3af4bb75107c413f71abe7f063b7d8de Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 17 Mar 2025 11:52:44 +0000 Subject: [PATCH 53/66] tidying POMs --- all/pom.xml | 2 +- basic/pom.xml | 30 +++++++++++++++++------------- kdb/pom.xml | 2 +- kdbx-database/pom.xml | 2 +- kdbx-io/pom.xml | 5 +---- pom.xml | 11 ++++++----- 6 files changed, 27 insertions(+), 25 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 261d94e4..ba081180 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -28,7 +28,7 @@ KeePassJava2 KeePassJava2 :: All - KDBX and KDB Database support. + Composite containing KDBX and KDB Database support diff --git a/basic/pom.xml b/basic/pom.xml index 9e5e0572..2efda25b 100644 --- a/basic/pom.xml +++ b/basic/pom.xml @@ -28,8 +28,24 @@ basic PWDB :: Basic + Experimental database implementation to explore serialization. + + true + + + org.linguafranca.pwdb + database + ${project.version} + compile + + + org.linguafranca.pwdb + KeePassJava2.kdbx.io + 3.0.0-SNAPSHOT + compile + com.fasterxml.jackson.dataformat jackson-dataformat-xml @@ -45,24 +61,12 @@ woodstox-core 7.1.0 - - org.linguafranca.pwdb - database - ${project.version} - compile - org.linguafranca.pwdb database ${project.version} test-jar - - - org.linguafranca.pwdb - KeePassJava2.kdbx.io - 3.0.0-SNAPSHOT - compile + test - \ No newline at end of file diff --git a/kdb/pom.xml b/kdb/pom.xml index 29a3cb80..5d2cb753 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -27,7 +27,7 @@ KeePassJava2.kdb KeePassJava2 :: KDB - Contains an implementation that can load KDB files. + KeePass KDB file support diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index b47dff6d..d4876c45 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -26,7 +26,7 @@ KeePassJava2.kdbx.database KeePassJava2 :: KDBX-Database - Contains a Jackson XML implementation for KDBX. + KeePass KDBX file support diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 32bcbeb4..96444de8 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -27,10 +27,7 @@ KeePassJava2.kdbx.io KeePassJava2 :: KDBX-IO - Base classes and resources for other KDBX modules. - KDBX Stream Support. - Security support. - + KeePass KDBX File format support diff --git a/pom.xml b/pom.xml index cc90baf2..75632e49 100644 --- a/pom.xml +++ b/pom.xml @@ -35,8 +35,7 @@ pom KeePassJava2 - A Java 11 API for databases compatible with the renowned KeePass password safe for Windows - + A Java 11 API for databases compatible with the renowned KeePass password safe for Windows https://github.com/jorabin/KeePassJava2 @@ -99,10 +98,9 @@ ${inhibitConsoleOutput} - + - ${maven.multiModuleProjectDirectory}/logback.xml - + ${maven.multiModuleProjectDirectory}/logback.xml @@ -263,12 +261,15 @@ + inhibitConsoleOutput true + releaseToMaven From a4ba09d19a27d480ab2108f10a5c555a8777972c Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 17 Mar 2025 13:00:13 +0000 Subject: [PATCH 54/66] Tweaking tests and examples --- .../linguafranca/util/ExecutionListener.java | 4 +- .../java/org/linguafranca/util/TestUtil.java | 37 +++++++++++++++++-- .../linguafranca/pwdb/kdbx/OpenDbExample.java | 13 ++++--- .../linguafranca/pwdb/kdbx/QuickStart.java | 16 ++++++++ .../org/linguafranca/pwdb/kdbx/SaxParse.java | 6 +++ .../pwdb/kdbx/SimpleQuickStartTest.java | 10 +++++ 6 files changed, 75 insertions(+), 11 deletions(-) diff --git a/database/src/test/java/org/linguafranca/util/ExecutionListener.java b/database/src/test/java/org/linguafranca/util/ExecutionListener.java index fcbebf54..1c798048 100644 --- a/database/src/test/java/org/linguafranca/util/ExecutionListener.java +++ b/database/src/test/java/org/linguafranca/util/ExecutionListener.java @@ -24,7 +24,7 @@ public class ExecutionListener implements TestExecutionListener { - static Logger logger = LoggerFactory.getLogger("MavenRunListener"); + static Logger logger = LoggerFactory.getLogger("Test Execution Listener"); static Marker marker = MarkerFactory.getMarker("Starting"); static Marker endMarker = MarkerFactory.getMarker("Finished"); static Marker failMarker = MarkerFactory.getMarker("Failed"); @@ -39,7 +39,7 @@ public void executionSkipped(TestIdentifier testIdentifier, String reason) { @Override public void executionStarted(TestIdentifier testIdentifier) { - logger.info(marker, "{}", testIdentifier.getDisplayName()); + logger.info(marker, "{} {}", testIdentifier.getType(), testIdentifier.getDisplayName()); } @Override diff --git a/database/src/test/java/org/linguafranca/util/TestUtil.java b/database/src/test/java/org/linguafranca/util/TestUtil.java index 7d562ca0..7d5eb078 100644 --- a/database/src/test/java/org/linguafranca/util/TestUtil.java +++ b/database/src/test/java/org/linguafranca/util/TestUtil.java @@ -23,18 +23,49 @@ public class TestUtil { + /** + * Database save closes output stream. If you want to output to console using KdbxFormat.None and System.out + * then use this class to wrap System.out, so that it doesn't get closed. + */ + public static class UncloseableOutputStream extends OutputStream { + + private final OutputStream outputStream; + + public UncloseableOutputStream(OutputStream outputStream) { + this.outputStream = outputStream; + } + + @Override + public void write(int b) throws IOException { + outputStream.write(b); + } + + @Override + public void close() throws IOException { + // do nothing + } + } + + /** + * Null output stream, i.e. throw away everything that is written to it. Useful during builds + * when you don't want to see the output - e.g. CI builds. + */ public static class NullOutputStream extends OutputStream { @Override public void write(int b) throws IOException {} } + /** - * set system property to suppress output from tests - * @return if "inhibitConsoleOutput" has been set, e.g. in a profile + * Get a print stream that will not close System.out, but will throw away all output + * if "inhibitConsoleOutput" system property has been set - e.g. as a result of using + * mvn clean install -P inhibitConsoleOutput. */ public static PrintStream getTestPrintStream() { return Boolean.getBoolean("inhibitConsoleOutput") ? + // throw away all output new PrintStream(new NullOutputStream()) : - new PrintStream(System.out); + // never close System.out + new PrintStream(new UncloseableOutputStream(System.out)); } } \ No newline at end of file diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index 06838e74..b9177d87 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -30,7 +30,8 @@ /** * Example and naive speed test for various implementations. *

- * This isn't really very interesting in V3 but remains here in case new implementations are added. + * It was used to compare the various implementations of KDBX in V2. It isn't really very interesting in V3 but + * remains here in case new implementations are added. * * @author jo */ @@ -42,7 +43,7 @@ private interface DbLoader { Database load(KdbxCredentials creds, InputStream inputStream) throws Exception; } - private static class JacksonDbLoader implements DbLoader { + private static class KdbxLoader implements DbLoader { @Override public Database load(KdbxCredentials creds, InputStream inputStream) throws Exception { return KdbxDatabase.load(creds, inputStream); @@ -67,24 +68,24 @@ private static void testDb (DbLoader loader, String label, int loads, int iterat public static void main(String[] args) throws Exception { printStream.println("Warming up JVM"); - testDb(new JacksonDbLoader(), "Jackson", 5, 20); + testDb(new KdbxLoader(), "Jackson", 5, 20); printStream.println("Sleeping"); System.gc(); Thread.sleep(2000); - testDb(new JacksonDbLoader(), "Jackson", 5, 20); + testDb(new KdbxLoader(), "Jackson", 5, 20); printStream.println("Sleeping"); System.gc(); Thread.sleep(2000); - testDb(new JacksonDbLoader(), "Jackson", 10, 1); + testDb(new KdbxLoader(), "Jackson", 10, 1); printStream.println("Sleeping"); System.gc(); Thread.sleep(2000); - testDb(new JacksonDbLoader(), "Jackson", 1, 50); + testDb(new KdbxLoader(), "Jackson", 1, 50); } } diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index b01c8a79..643fac02 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -25,6 +25,7 @@ import org.linguafranca.pwdb.kdb.KdbDatabase; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.security.Encryption; +import org.linguafranca.util.TestUtil; import java.io.*; import java.nio.file.Files; @@ -256,4 +257,19 @@ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3File kdbxDatabase.save(formatV3, credentials, v3OutputStream); } } + + /** + * Load KDBX V3 save as KDBX V4 + */ + public void loadKdbx4SaveToConsole(String resourceName, byte[] password, Path path) throws IOException { + KdbxDatabase database; + // password credentials + KdbxCredentials credentials = new KdbxCredentials(password); + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { + // load KdbDatabase + database = KdbxDatabase.load(credentials, inputStream); + } + + database.save(new StreamFormat.None(), new Credentials.None(), TestUtil.getTestPrintStream()); + } } diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java index a21a8783..864b0dd0 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/SaxParse.java @@ -33,6 +33,12 @@ import java.io.PrintWriter; /** + * NB if you want to examine the XML of a KeePass file it's easiest to use
+ * + * database.save(new KdbxFormat.None(), new Credentials.None(), new PrintWriter(System.out)); + * + *

+ * See {@link QuickStart#loadKdbx4SaveToConsole} * @author jo */ public class SaxParse { diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index d2ee72ec..c19b1d1a 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -44,6 +44,16 @@ public static void ensureOutputDir() throws IOException { Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); } + @Test + public void canonicalQuickstartTest() throws IOException { + super.canonicalQuickStart(); + } + + @Test + public void fluentExampleTest() throws IOException { + super.fluentExample(); + } + @Test public void saveTest() throws IOException { super.saveKdbx(); From dd9ba0aa7d98cf4344a5841f2e10d7d736947cb5 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Mon, 17 Mar 2025 14:08:35 +0000 Subject: [PATCH 55/66] try saving BasicDatabase as KDBX format, minor tweaks as well --- .../pwdb/basic/BasicDatabase.java | 3 +- .../pwdb/basic/BasicDatabaseKdbxTest.java | 65 +++++++++++++++++++ .../basic/BasicDatabaseSerializerTest.java | 12 ++-- .../pwdb/basic/BasicDatabaseTest.java | 12 ++-- .../pwdb/test/DatabaseTestBase.java | 2 +- .../linguafranca/pwdb/format/KdbxHeader.java | 6 +- .../pwdb/format/KdbxStreamFormat.java | 4 +- 7 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 955dc47c..7bab2ce8 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -28,7 +28,6 @@ import java.io.OutputStream; import java.time.Instant; import java.util.Objects; -import java.util.Stack; /** * A basic implementation of a Database for experimentation and testing @@ -114,7 +113,7 @@ public void save(StreamFormat streamFormat, KdbxStreamFormat kdbxStreamFormat = (KdbxStreamFormat) streamFormat; KdbxHeader header = kdbxStreamFormat.getStreamConfiguration(); OutputStream encryptedOutputStream = KdbxSerializer.createEncryptedOutputStream(credentials, header, outputStream); - BasicDatabaseSerializer bds = new BasicDatabaseSerializer.Xml(header.getStreamEncryptor()); + BasicDatabaseSerializer bds = new BasicDatabaseSerializer.Xml(header.getInnerStreamEncryptor()); bds.save(this, encryptedOutputStream); } diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java new file mode 100644 index 00000000..3db445ea --- /dev/null +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.basic; + +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; +import org.linguafranca.pwdb.format.KdbxStreamFormat; +import org.linguafranca.pwdb.test.DatabaseTestBase; + +import java.io.*; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; + +public class BasicDatabaseKdbxTest extends + DatabaseTestBase { + + public BasicDatabaseKdbxTest(){ + super(BasicDatabase::new, + (credentials, inputStream) -> + new BasicDatabaseSerializer.Xml().loadNx(inputStream), + (database, credentials, outputStream) -> + new BasicDatabaseSerializer.Xml().saveNx((BasicDatabase) database, outputStream), + (credentials) -> null); + } + + @Test + public void testKdbx() throws IOException { + BasicDatabase db = new BasicDatabase(); + db.setName("test"); + db.getRootGroup().addEntry("test") + .addProperty(PASSWORD,"password") + .addProperty(URL,"http://example.com") + .addProperty(USER_NAME,"user"); + db.save(new KdbxStreamFormat(), new KdbxCredentials("123".getBytes()), new FileOutputStream(Path.of(OUTPUT_DIRECTORY_PATH, "test.kdbx").toFile())); + + KdbxHeader header = new KdbxHeader(); + InputStream unencryptedInputStream = KdbxSerializer.createUnencryptedInputStream(new KdbxCredentials("123".getBytes()), + header, new FileInputStream(Path.of(OUTPUT_DIRECTORY_PATH, "test.kdbx").toFile())); + BasicDatabaseSerializer.Xml xml = new BasicDatabaseSerializer.Xml(header.getInnerStreamEncryptor()); + Database db2 = xml.load(unencryptedInputStream); + assertEquals(db.getName(), db2.getName()); + assertEquals(db2.getRootGroup().findEntries(e -> true, true).size(), db.getRootGroup().findEntries(e -> true, true).size()); + assertEquals("password", db2.getRootGroup().findEntries(e -> e.getTitle().equals("test"), true).get(0).getPropertyValue(PASSWORD).getValueAsString()); + } +} diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java index 415c9e23..4b647c67 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java @@ -31,10 +31,12 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.linguafranca.util.TestUtil.getTestPrintStream; class BasicDatabaseSerializerTest { public static final String TEST_OUTPUT_DIR = "testOutput/"; static BasicDatabase database; + static PrintStream out = getTestPrintStream(); @BeforeAll static void setUp() throws IOException { @@ -57,7 +59,7 @@ static void setUp() throws IOException { Visitor visitor = new Visitor() { @Override public void startVisit(Group group) { - System.out.println("Group: " + group.getName() + " " + group.getUuid()); + out.println("Group: " + group.getName() + " " + group.getUuid()); assertNotNull(database.findGroup(group.getUuid())); assertNotNull(group.getDatabase()); if (!group.isRootGroup()) { @@ -72,12 +74,12 @@ public void endVisit(Group group) { @Override public void visit(Entry entry) { - System.out.println("Entry: " + entry.getTitle() + " " + entry.getUuid()); + out.println("Entry: " + entry.getTitle() + " " + entry.getUuid()); for (String propertyName: entry.getPropertyNames()) { - System.out.println(" " + propertyName + ": " + entry.getPropertyValue(propertyName).getValueAsString()); + out.println(" " + propertyName + ": " + entry.getPropertyValue(propertyName).getValueAsString()); } for (String propertyName: entry.getBinaryPropertyNames()) { - System.out.println(" " + propertyName + ": " + Arrays.toString(entry.getBinaryProperty(propertyName))); + out.println(" " + propertyName + ": " + Arrays.toString(entry.getBinaryProperty(propertyName))); } Entry entry1 = database.findEntry(entry.getUuid()); assertNotNull(entry1, "Entry not found " + entry.getUuid()); @@ -97,6 +99,6 @@ void saveAndLoad() throws IOException { new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).save(database, new FileOutputStream(TEST_OUTPUT_DIR + "test.xml")); BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); loadedDatabase.visit(visitor); - System.out.println(loadedDatabase.getRootGroup().getDatabase().getName()); + out.println(loadedDatabase.getRootGroup().getDatabase().getName()); } } \ No newline at end of file diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java index f0d07c09..156cb343 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java @@ -30,11 +30,11 @@ public class BasicDatabaseTest ProtectedPropertyTest2 { BasicDatabaseTest() { -super(BasicDatabase::new, - (credentials, inputStream) -> - new BasicDatabaseSerializer.Xml().loadNx(inputStream), - (database, credentials, outputStream) -> - new BasicDatabaseSerializer.Xml().saveNx((BasicDatabase) database, outputStream), - (credentials) -> null); + super(BasicDatabase::new, + (credentials, inputStream) -> + new BasicDatabaseSerializer.Xml().loadNx(inputStream), + (database, credentials, outputStream) -> + new BasicDatabaseSerializer.Xml().saveNx((BasicDatabase) database, outputStream), + (credentials) -> null); } } diff --git a/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java b/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java index 8f09a16f..713a3b6d 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java @@ -46,7 +46,7 @@ default TriConsumer andThen(TriConsumer Date: Tue, 17 Mar 2026 17:40:34 +0000 Subject: [PATCH 56/66] Fixes for #87 and 89 --- .../pwdb/kdbx/jackson/KdbxEntry.java | 23 +++++ .../pwdb/kdbx/jackson/model/EntryClasses.java | 2 + .../pwdb/kdbx/jackson/model/KeePassFile.java | 24 +++--- .../kdbx/database/validation/Issue87Test.java | 79 ++++++++++++++++++ .../kdbx/database/validation/Issue88Test.java | 49 +++++++++++ .../resources/issue-87/customIcon-123.kdbx | Bin 0 -> 30919 bytes .../test/resources/issue-88/newDb-123.kdbx | Bin 0 -> 1959 bytes 7 files changed, 167 insertions(+), 10 deletions(-) create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java create mode 100644 kdbx-database/src/test/resources/issue-87/customIcon-123.kdbx create mode 100644 kdbx-database/src/test/resources/issue-88/newDb-123.kdbx diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index 2da9aa53..63ee03b5 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -340,6 +340,29 @@ public void setIcon(Icon icon) { iconID = icon.getIndex(); } + @JsonIgnore + public UUID getCustomIconUuid() { + return customIconUUID; + } + + @JsonIgnore + public void setCustomIconUUID(UUID uuid) { + customIconUUID = uuid; + } + + @JsonIgnore + public KeePassFile.Icon getCustomIcon() { + if (customIconUUID == null) { + throw new IllegalStateException("Custom icon has not been set"); + } + for (KeePassFile.Icon icon : database.keePassFile.meta.customIcons) { + if (icon.uuid.equals(customIconUUID)) { + return icon; + } + } + throw new IllegalStateException("Custom icon was not found in database for uuid: " + customIconUUID); + } + @Override @JsonIgnore public Date getLastAccessTime() { diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java index 8b7b68c0..44d26864 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/EntryClasses.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.List; +import com.fasterxml.jackson.annotation.JsonInclude; import org.linguafranca.pwdb.PropertyValue; import org.linguafranca.pwdb.kdbx.jackson.KdbxEntry; import org.linguafranca.pwdb.kdbx.jackson.converter.BooleanToStringConverter; @@ -73,6 +74,7 @@ public static class Association { @JacksonXmlProperty(localName = "Window") protected String window; @JacksonXmlProperty(localName = "KeystrokeSequence") + @JsonInclude(JsonInclude.Include.ALWAYS) protected String keystrokeSequence; protected List windowAndKeystrokeSequence; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java index 3e46882f..0a49aed0 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/model/KeePassFile.java @@ -72,6 +72,7 @@ public Root() { @JsonPropertyOrder({ "generator", + "settingsChanged", "headerHash", "databaseName", "databaseNameChanged", @@ -96,7 +97,7 @@ public Root() { "lastTopVisibleGroup", "historyMaxItems", "historyMaxSize", - "binaries", + "customData", }) public static class Meta { @@ -161,7 +162,8 @@ public Meta() { public KeePassFile.MemoryProtection memoryProtection; @JacksonXmlElementWrapper(localName = "CustomIcons") - protected ArrayList customIcons; + @JacksonXmlProperty(localName = "Icon") + public ArrayList customIcons; @JacksonXmlProperty(localName = "RecycleBinEnabled") @JsonDeserialize(converter = StringToBooleanConverter.class) @@ -317,6 +319,12 @@ public void setCompressed(boolean compressed) { } } + @JsonPropertyOrder({ + "uuid", + "data", + "name", + "lastModificationTime", + }) public static class Icon { public Icon() { } @@ -324,24 +332,20 @@ public Icon() { @JacksonXmlProperty(localName = "UUID") @JsonDeserialize(converter = Base64ToUUIDConverter.class) @JsonSerialize(converter = UUIDToBase64Converter.class) - protected UUID uuid; + public UUID uuid; @JacksonXmlProperty(localName = "Name") - protected String name; + public String name; @JacksonXmlProperty(localName = "LastModificationTime") @JsonDeserialize(converter = StringToDateConverter.class) @JsonSerialize(converter = DateToStringConverter.class) - protected Date lastModificationTime; + public Date lastModificationTime; @JacksonXmlProperty(localName = "Data") @JsonDeserialize(converter = Base64ToByteConverter.class) @JsonSerialize(converter = ByteToBase64Converter.class) - protected byte[] data; - - public String getName() { - return uuid.toString(); - } + public byte[] data; } public static class CustomData { diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java new file mode 100644 index 00000000..844f21dc --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java @@ -0,0 +1,79 @@ +package org.linguafranca.pwdb.kdbx.database.validation; + +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.KdbxEntry; +import org.linguafranca.pwdb.kdbx.jackson.model.KeePassFile; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.UUID; + +import static org.junit.jupiter.api.Assertions.*; +import static org.linguafranca.pwdb.kdbx.jackson.util.Util.streamToString; + +/** + * Review Issue-87 ... + */ +public class Issue87Test { + + + public static final String TEST_RESOURCE1 = "issue-87/customIcon-123.kdbx"; + public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials("123".getBytes()); + public static final String TEST_OUTPUT_ISSUE_87_KDBX = "testOutput/Issue87.kdbx"; + + + @Test + public void testCustomIcon() throws IOException { + KdbxDatabase database; + try (InputStream inputStream = Issue87Test.class.getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + database = KdbxDatabase.load(CREDENTIALS1, inputStream); + } + + List entries = database.findEntries("Michael321"); + assertEquals(1, entries.size()); + + KdbxEntry entry = (KdbxEntry) entries.get(0); + UUID customIcon = entry.getCustomIconUuid(); + assertNotNull(customIcon); + System.out.println("Custom icon id: " + customIcon); + KeePassFile.Icon icon = entry.getCustomIcon(); + + Path path = Paths.get(TEST_OUTPUT_ISSUE_87_KDBX); + try (OutputStream outputStream = Files.newOutputStream(path)) { + database.save(CREDENTIALS1, outputStream); + } + + // output the XML of the file we just saved, to check that the custom icon is still there + try (InputStream is = Files.newInputStream(path); + InputStream ss = KdbxSerializer.createUnencryptedInputStream(CREDENTIALS1, new KdbxHeader(), is)) { + System.out.println(streamToString(ss)); + System.out.println(); + System.out.flush(); + } + + // now load the newly written database and check the custom icon is the same as the original + try (InputStream is = Files.newInputStream(path)) { + KdbxDatabase database1 = KdbxDatabase.load(CREDENTIALS1, is); + List entries1 = database1.findEntries("Michael321"); + assertEquals(1, entries1.size()); + KdbxEntry entry1 = (KdbxEntry) entries1.get(0); + UUID customIcon1 = entry1.getCustomIconUuid(); + assertNotNull(customIcon1); + assertEquals(customIcon, customIcon1); + KeePassFile.Icon icon1 = entry1.getCustomIcon(); + assertEquals(icon1.name, icon.name); + assertArrayEquals(icon1.data, icon.data); + assertEquals(icon1.uuid, icon.uuid); + } + } +} \ No newline at end of file diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java new file mode 100644 index 00000000..f5324c39 --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java @@ -0,0 +1,49 @@ +package org.linguafranca.pwdb.kdbx.database.validation; + +import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.format.KdbxCredentials; +import org.linguafranca.pwdb.format.KdbxHeader; +import org.linguafranca.pwdb.format.KdbxSerializer; +import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.linguafranca.pwdb.kdbx.jackson.util.Util.streamToString; + +/** + * Review Issue-88 ... + */ +public class Issue88Test { + + + public static final String TEST_RESOURCE1 = "issue-88/newDb-123.kdbx"; + public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials("123".getBytes()); + public static final String TEST_OUTPUT_ISSUE_88_KDBX = "testOutput/Issue88.kdbx"; + + + @Test + public void testDefaultAutoType() throws IOException { + KdbxDatabase database; + try (InputStream inputStream = Issue88Test.class.getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + database = KdbxDatabase.load(CREDENTIALS1, inputStream); + } + + Path path = Paths.get(TEST_OUTPUT_ISSUE_88_KDBX); + try (OutputStream outputStream = Files.newOutputStream(path)) { + database.save(CREDENTIALS1, outputStream); + } + + // output the XML of the file we just saved, to check that the empty element is serialized + try (InputStream is = Files.newInputStream(path); + InputStream ss = KdbxSerializer.createUnencryptedInputStream(CREDENTIALS1, new KdbxHeader(), is)) { + System.out.println(streamToString(ss)); + System.out.println(); + System.out.flush(); + } + } +} \ No newline at end of file diff --git a/kdbx-database/src/test/resources/issue-87/customIcon-123.kdbx b/kdbx-database/src/test/resources/issue-87/customIcon-123.kdbx new file mode 100644 index 0000000000000000000000000000000000000000..0d46aab039753e7622163898b516c22cdc2414dc GIT binary patch literal 30919 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 literal 0 HcmV?d00001 From e080339e8a5f379919fac7db72ebaeb86f1d269a Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 17 Mar 2026 17:53:54 +0000 Subject: [PATCH 57/66] Updating dependencies --- database/pom.xml | 5 +++++ kdbx-database/pom.xml | 4 ++-- pom.xml | 13 ++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/database/pom.xml b/database/pom.xml index fbb83d91..9992eabc 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -39,6 +39,11 @@ org.bouncycastle bcpkix-jdk18on + + com.google.code.findbugs + jsr305 + 3.0.2 + diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index d4876c45..11406615 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -37,12 +37,12 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - 2.18.2 + 2.21.1 com.fasterxml.woodstox woodstox-core - 7.1.0 + 7.1.1 org.linguafranca.pwdb diff --git a/pom.xml b/pom.xml index 75632e49..236eb3d7 100644 --- a/pom.xml +++ b/pom.xml @@ -219,35 +219,34 @@ org.jetbrains annotations - 26.0.1 + 26.1.0 - com.google.guava guava - 33.4.0-jre + 33.5.0-jre org.bouncycastle bcpkix-jdk18on - 1.79 + 1.83 org.slf4j slf4j-api - 2.0.16 + 2.0.17 ch.qos.logback logback-classic - 1.5.15 + 1.5.32 commons-codec commons-codec - 1.17.1 + 1.21.0 org.junit From 74f51e03d78caee850a89b3f9448d10b7bdfe524 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 17 Mar 2026 18:11:24 +0000 Subject: [PATCH 58/66] Update changelog and change SNAPSHOT repo location --- CHANGELOG.md | 4 ++++ pom.xml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aba7fdf7..c6df680d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,10 @@ Trying to follow the suggestions at [Keep a Change Log](http://keepachangelog.co ### Changed +- [Issue 81, 83] Remove junit dependency from main code and resolve test failure from UTF-8 encoding in test resources +- [Issue 87] Problem with incorrect serialization of CustomIcons in Jackson implementation +- [Issue 88] Incompatibility to KeePass due t missing empty element in autotype field in Jackson implementation +- [Issue 90] Update dependencies to resolve security vulnerabilities - update to Java 11 - refactor API - remove complicated generics diff --git a/pom.xml b/pom.xml index 236eb3d7..f9fcf442 100644 --- a/pom.xml +++ b/pom.xml @@ -333,8 +333,8 @@ - ossrh - https://oss.sonatype.org/content/repositories/snapshots + central + https://central.sonatype.com/repository/maven-snapshots/ From e59eb20e4a72010cef4b70359acec21464e12cf4 Mon Sep 17 00:00:00 2001 From: Jo Rabin Date: Tue, 17 Mar 2026 18:19:12 +0000 Subject: [PATCH 59/66] Update readme to change SNAPSHOT repo location --- readme.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/readme.md b/readme.md index 276a768b..19e87fdf 100644 --- a/readme.md +++ b/readme.md @@ -80,16 +80,16 @@ with appropriate `` entry, like: - oss.sonatype.org-snapshot - https://oss.sonatype.org/content/repositories/snapshots - - false - - - true - + oss.sonatype.org-snapshot + https://central.sonatype.com/repository/maven-snapshots/ + + false + + + true + - + The module structure is illustrated below under [Build from Source](#build-from-source). From e2ff6229b905a9398474fb8f01d6c7fa6bb203aa Mon Sep 17 00:00:00 2001 From: sergmain Date: Fri, 8 May 2026 08:54:51 -0700 Subject: [PATCH 60/66] switched to jspecify --- .editorconfig | 23 +++++++++++ .gitattributes | 39 +++++++++++++++++++ .gitignore | 1 + .../pwdb/basic/BasicDatabase.java | 2 +- .../linguafranca/pwdb/basic/BasicEntry.java | 15 ++++--- .../linguafranca/pwdb/basic/BasicGroup.java | 7 ++-- .../linguafranca/pwdb/basic/package-info.java | 28 +++++++++++++ .../java/org/linguafranca/pwdb/Database.java | 2 +- .../java/org/linguafranca/pwdb/Entry.java | 9 ++--- .../pwdb/abstractdb/package-info.java | 20 ++++++++++ .../org/linguafranca/pwdb/package-info.java | 5 ++- .../pwdb/protect/package-info.java | 20 ++++++++++ .../pwdb/security/VariantDictionary.java | 29 +++++++------- .../pwdb/security/package-info.java | 20 ++++++++++ .../org/linguafranca/pwdb/kdb/KdbEntry.java | 6 +-- .../linguafranca/pwdb/kdb/package-info.java | 28 +++++++++++++ .../pwdb/kdbx/jackson/KdbxEntry.java | 4 +- .../pwdb/kdbx/jackson/package-info.java | 28 +++++++++++++ .../pwdb/format/KdbxCredentials.java | 7 ++-- .../linguafranca/pwdb/format/KdbxCreds.java | 8 ++-- .../pwdb/format/package-info.java | 28 +++++++++++++ .../hashedblock/CollectingInputStream.java | 6 +-- .../hashedblock/CollectingOutputStream.java | 6 +-- .../hashedblock/HashedBlockInputStream.java | 4 +- .../hashedblock/HashedBlockOutputStream.java | 4 +- .../hashedblock/HmacBlockOutputStream.java | 3 +- .../pwdb/hashedblock/package-info.java | 28 +++++++++++++ pom.xml | 16 ++++++++ 28 files changed, 330 insertions(+), 66 deletions(-) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 basic/src/main/java/org/linguafranca/pwdb/basic/package-info.java create mode 100644 database/src/main/java/org/linguafranca/pwdb/abstractdb/package-info.java create mode 100644 database/src/main/java/org/linguafranca/pwdb/protect/package-info.java create mode 100644 database/src/main/java/org/linguafranca/pwdb/security/package-info.java create mode 100644 kdb/src/main/java/org/linguafranca/pwdb/kdb/package-info.java create mode 100644 kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java create mode 100644 kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java create mode 100644 kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..721db89e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +# Editor configuration, see https://editorconfig.org +root = true + +[*.java] +charset = utf-8 +indent_style = space +indent_size = 4 +# DO NOT USE insert_final_newline = true +#insert_final_newline = true + +[*.yaml] +indent_size = 2 +indent_style = space + +[*.sh] +end_of_line = lf + +[*.cmd] +end_of_line = crlf + +[*.py] +indent_size = 4 +indent_style = space \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..1ddff4b5 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,39 @@ +* -text + +*.css text +*.scss text +*.sass text +*.html text +*.java text +*.js text +*.json text +*.properties text +*.txt text +*.xml text +*.py text +*.cmd text +*.bat text +*.yaml text +*.yml text +*.sql text +# *.ts text + +.gitignore text +.gitattributes text +.slugignore text +.editorconfig text + +COPYING text +README.md text +mvnw text + + +# These files are binary and should be left untouched +*.class binary +*.jar binary +*.gif binary +*.jpg binary +*.png binary +*.ico binary +*.mp4 binary +*.xcf binary diff --git a/.gitignore b/.gitignore index 3ee52c53..e918a7f4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build .vscode/* *.versionsBackup **/target +.idea #settings.gradle #gradlew diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java index 7bab2ce8..7abd4f4f 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicDatabase.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.basic; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.Nullable; import org.linguafranca.pwdb.*; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java index fa9653b8..adcbafdd 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicEntry.java @@ -17,8 +17,7 @@ package org.linguafranca.pwdb.basic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.Nullable; import org.linguafranca.pwdb.*; import org.linguafranca.pwdb.abstractdb.AbstractEntry; @@ -35,8 +34,8 @@ public class BasicEntry extends AbstractEntry { private final Map binaries = new HashMap<>(); private final Instant creationTime; - private Instant accessTime; - private Instant modifiedTime; + private @Nullable Instant accessTime; + private @Nullable Instant modifiedTime; private Instant expiryTime; private boolean isExpires; @@ -61,7 +60,7 @@ protected void touch() { } @Override - public String getProperty(String name) { + public @Nullable String getProperty(String name) { if (!STANDARD_PROPERTY_NAMES.contains(name) && !database.supportsNonStandardPropertyNames()) { throw new IllegalArgumentException("Property " + name + " is not a standard property name"); } @@ -125,7 +124,7 @@ public BasicEntry addProperty(String name, CharSequence value){ return this; } @Override - public byte[] getBinaryProperty(String name) { + public byte@Nullable [] getBinaryProperty(String name) { if (!binaries.containsKey(name)) { return null; } @@ -163,7 +162,7 @@ public BasicDatabase getDatabase() { } @Override - public @NotNull UUID getUuid() { + public UUID getUuid() { return uuid; } @@ -228,7 +227,7 @@ private void updateModifiedTime() { touch(); } - private Date fromInstant(Instant instant) { + private static @Nullable Date fromInstant(@Nullable Instant instant) { if (instant == null) { return null; } diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java index e1bdc41a..adde7c00 100644 --- a/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/BasicGroup.java @@ -17,8 +17,7 @@ package org.linguafranca.pwdb.basic; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.Nullable; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; @@ -48,12 +47,12 @@ public class BasicGroup extends AbstractGroup { this.icon = new BasicIcon(); this.name = ""; } - BasicGroup(@NotNull BasicDatabase database) { + BasicGroup(BasicDatabase database) { this(); this.database = database; } - BasicGroup(@NotNull BasicDatabase database, String name) { + BasicGroup(BasicDatabase database, String name) { this(database); this.name = name; } diff --git a/basic/src/main/java/org/linguafranca/pwdb/basic/package-info.java b/basic/src/main/java/org/linguafranca/pwdb/basic/package-info.java new file mode 100644 index 00000000..21f36f62 --- /dev/null +++ b/basic/src/main/java/org/linguafranca/pwdb/basic/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * This package contains the interfaces that define the org.linguafranca Database API. + *

+ * The API is designed to be a simple, low-level, read-write API for password databases. + *

+ * Class instances of the interfaces are contained in the sub-packages of this package. + */ +@NullMarked +package org.linguafranca.pwdb.basic; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/Database.java b/database/src/main/java/org/linguafranca/pwdb/Database.java index e11c6bc9..a92231a7 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Database.java +++ b/database/src/main/java/org/linguafranca/pwdb/Database.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.io.OutputStream; diff --git a/database/src/main/java/org/linguafranca/pwdb/Entry.java b/database/src/main/java/org/linguafranca/pwdb/Entry.java index 0beb636e..1d7acf64 100644 --- a/database/src/main/java/org/linguafranca/pwdb/Entry.java +++ b/database/src/main/java/org/linguafranca/pwdb/Entry.java @@ -17,8 +17,7 @@ package org.linguafranca.pwdb; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.Nullable; import java.util.*; @@ -115,7 +114,7 @@ interface Matcher { * @return a value or null if the property is not known, or if setting of arbitrary properties is not supported * @see Database#supportsNonStandardPropertyNames() */ - String getProperty(String name); + @Nullable String getProperty(String name); /** * Gets the value of a property as a PropertyValue. @@ -247,7 +246,7 @@ interface Matcher { * @return a value or null if the property is not known, or if setting of arbitrary properties is not supported * @see Database#supportsBinaryProperties */ - byte[] getBinaryProperty(String name); + byte@Nullable [] getBinaryProperty(String name); /** * Sets the value of a binary property. @@ -300,7 +299,7 @@ interface Matcher { * * @return a UUID */ - @NotNull UUID getUuid(); + UUID getUuid(); /** * Get the username field of this entry diff --git a/database/src/main/java/org/linguafranca/pwdb/abstractdb/package-info.java b/database/src/main/java/org/linguafranca/pwdb/abstractdb/package-info.java new file mode 100644 index 00000000..6aafe5e8 --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/abstractdb/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@NullMarked +package org.linguafranca.pwdb.abstractdb; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/package-info.java b/database/src/main/java/org/linguafranca/pwdb/package-info.java index a493b42c..bf6b8ed1 100644 --- a/database/src/main/java/org/linguafranca/pwdb/package-info.java +++ b/database/src/main/java/org/linguafranca/pwdb/package-info.java @@ -22,4 +22,7 @@ *

* Class instances of the interfaces are contained in the sub-packages of this package. */ -package org.linguafranca.pwdb; \ No newline at end of file +@NullMarked +package org.linguafranca.pwdb; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/protect/package-info.java b/database/src/main/java/org/linguafranca/pwdb/protect/package-info.java new file mode 100644 index 00000000..acad64db --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/protect/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@NullMarked +package org.linguafranca.pwdb.protect; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java b/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java index a932d20a..11b0580c 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java @@ -17,8 +17,7 @@ package org.linguafranca.pwdb.security; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import org.jspecify.annotations.Nullable; import javax.annotation.concurrent.Immutable; import java.nio.ByteBuffer; @@ -79,14 +78,14 @@ public static EntryType get(byte type) { @Immutable public static class Entry { private final byte type; - private final byte @NotNull [] value; + private final byte [] value; private final ByteOrder byteOrder; - public Entry(EntryType entryType, byte @NotNull [] value) { + public Entry(EntryType entryType, byte[] value) { this(entryType, value, ByteOrder.LITTLE_ENDIAN); } - public Entry(EntryType entryType, byte @NotNull [] value, ByteOrder byteOrder) { + public Entry(EntryType entryType, byte[] value, ByteOrder byteOrder) { this.type = entryType.value; this.value = checkNotNull(value, vnn); this.byteOrder = byteOrder; @@ -118,7 +117,7 @@ public int asInteger() { return ByteBuffer.wrap(value).order(byteOrder).getInt(); } - public byte @NotNull [] asByteArray() { + public byte[] asByteArray() { return value; } } @@ -164,7 +163,7 @@ public short getVersion() { * @param key the key * @return an entry, or null if no such entry exists */ - public @Nullable Entry get(@NotNull String key) { + public @Nullable Entry get(String key) { return entries.get(key); } @@ -174,7 +173,7 @@ public short getVersion() { * @param key the key to get * @return the entry corresponding to the key */ - public @NotNull Entry mustGet(@NotNull String key) { + public Entry mustGet(String key) { Entry entry = entries.get(key); if (entry == null) { throw new IllegalArgumentException("There is no entry with key " + key); @@ -189,14 +188,14 @@ public short getVersion() { * @param type the data type of the entry * @param value a buffer containing an appropriate entry */ - public void put(@NotNull String key, EntryType type, byte @NotNull [] value) { + public void put(String key, EntryType type, byte[] value) { entries.put(checkNotNull(key), new Entry(type, checkNotNull(value))); } /** * Put a UUID under the key defined */ - public void putUuid(@NotNull String key, UUID uuid) { + public void putUuid(String key, UUID uuid) { byte[] buf = new byte[16]; ByteBuffer bb = ByteBuffer.wrap(buf); bb.putLong(0, uuid.getMostSignificantBits()); @@ -207,14 +206,14 @@ public void putUuid(@NotNull String key, UUID uuid) { /** * Put a byte array under the key defined */ - public void putByteArray(@NotNull String key, byte @NotNull [] value) { + public void putByteArray(String key, byte[] value) { entries.put(checkNotNull(key, knn), new Entry(ARRAY, value)); } /** * Put a long as a signed64 under the key defined */ - public void putLong(@NotNull String key, long value) { + public void putLong(String key, long value) { byte[] buf = new byte[8]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putLong(value); @@ -223,20 +222,20 @@ public void putLong(@NotNull String key, long value) { /** * Put a long as an unsigned64 under the key defined */ - public void putULong(@NotNull String key, long value) { + public void putULong(String key, long value) { byte[] buf = new byte[8]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putLong(value); entries.put(checkNotNull(key, knn), new Entry(UINT64, buf)); } - public void putInt(@NotNull String key, int value) { + public void putInt(String key, int value) { byte[] buf = new byte[4]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putInt(value); entries.put(checkNotNull(key, knn), new Entry(INT32, buf)); } - public void putUInt(@NotNull String key, int value) { + public void putUInt(String key, int value) { byte[] buf = new byte[4]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putInt(value); diff --git a/database/src/main/java/org/linguafranca/pwdb/security/package-info.java b/database/src/main/java/org/linguafranca/pwdb/security/package-info.java new file mode 100644 index 00000000..61951d71 --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/security/package-info.java @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@NullMarked +package org.linguafranca.pwdb.security; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java index 1d5e903d..9b3a8603 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbEntry.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdb; -import org.jetbrains.annotations.NotNull; +import org.jspecify.annotations.Nullable; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Icon; @@ -52,7 +52,7 @@ public class KdbEntry extends AbstractEntry { private byte[] binaryData = new byte[0]; @Override - public String getProperty(String name) { + public @Nullable String getProperty(String name) { switch (name) { case STANDARD_PROPERTY_NAME_USER_NAME: return getUsername(); case STANDARD_PROPERTY_NAME_PASSWORD: return getPassword(); @@ -102,7 +102,7 @@ public KdbDatabase getDatabase() { } @Override - public @NotNull UUID getUuid() { + public UUID getUuid() { return uuid; } diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/package-info.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/package-info.java new file mode 100644 index 00000000..02f1b8de --- /dev/null +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * This package contains the interfaces that define the org.linguafranca Database API. + *

+ * The API is designed to be a simple, low-level, read-write API for password databases. + *

+ * Class instances of the interfaces are contained in the sub-packages of this package. + */ +@NullMarked +package org.linguafranca.pwdb.kdb; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java index 63ee03b5..7a85e2b8 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxEntry.java @@ -22,8 +22,6 @@ import java.util.List; import java.util.UUID; -import org.jetbrains.annotations.NotNull; - import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Icon; import org.linguafranca.pwdb.PropertyValue; @@ -326,7 +324,7 @@ public KdbxGroup getParent() { } @Override - public @NotNull UUID getUuid() { + public UUID getUuid() { return uuid; } diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java new file mode 100644 index 00000000..6fdcf327 --- /dev/null +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * This package contains the interfaces that define the org.linguafranca Database API. + *

+ * The API is designed to be a simple, low-level, read-write API for password databases. + *

+ * Class instances of the interfaces are contained in the sub-packages of this package. + */ +@NullMarked +package org.linguafranca.pwdb.kdbx.jackson; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java index d0497472..ab893a76 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCredentials.java @@ -17,7 +17,6 @@ package org.linguafranca.pwdb.format; -import org.jetbrains.annotations.NotNull; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.security.Encryption; @@ -38,7 +37,7 @@ public class KdbxCredentials implements Credentials { * @param password Master Password (new byte[0] if empty, not none) * @param inputStream inputStream of the keyfile */ - public KdbxCredentials(byte @NotNull [] password, @NotNull InputStream inputStream) { + public KdbxCredentials(byte[] password, InputStream inputStream) { MessageDigest md = Encryption.getSha256MessageDigestInstance(); byte[] pwKey = md.digest(password); md.update(pwKey); @@ -54,7 +53,7 @@ public KdbxCredentials(byte @NotNull [] password, @NotNull InputStream inputStre * Constructor for KDBX Keyfile with no password * @param inputStream inputStream of the keyfile */ - public KdbxCredentials(@NotNull InputStream inputStream) { + public KdbxCredentials(InputStream inputStream) { MessageDigest md = Encryption.getSha256MessageDigestInstance(); byte[] keyFileData = KdbxKeyFile.load(inputStream); if (keyFileData == null) { @@ -68,7 +67,7 @@ public KdbxCredentials(@NotNull InputStream inputStream) { * Constructor for password only * @param password Master Password for database (new byte[0] if empty, not none) */ - public KdbxCredentials(byte @NotNull [] password) { + public KdbxCredentials(byte[] password) { MessageDigest md = Encryption.getSha256MessageDigestInstance(); byte[] digest = md.digest(password); key = md.digest(digest); diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java index 7f8f5020..34a1f8a5 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxCreds.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb.format; -import org.jetbrains.annotations.NotNull; - import java.io.InputStream; /** @@ -28,15 +26,15 @@ */ @Deprecated public class KdbxCreds extends KdbxCredentials { - public KdbxCreds(byte @NotNull [] password, @NotNull InputStream inputStream) { + public KdbxCreds(byte[] password, InputStream inputStream) { super(password, inputStream); } - public KdbxCreds(@NotNull InputStream inputStream) { + public KdbxCreds(InputStream inputStream) { super(inputStream); } - public KdbxCreds(byte @NotNull [] password) { + public KdbxCreds(byte[] password) { super(password); } } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java new file mode 100644 index 00000000..ec08a0a2 --- /dev/null +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * This package contains the interfaces that define the org.linguafranca Database API. + *

+ * The API is designed to be a simple, low-level, read-write API for password databases. + *

+ * Class instances of the interfaces are contained in the sub-packages of this package. + */ +@NullMarked +package org.linguafranca.pwdb.format; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java index 493b4827..a8d85d55 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingInputStream.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb.hashedblock; -import org.jetbrains.annotations.NotNull; - import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; @@ -65,12 +63,12 @@ public int read() throws IOException { } @Override - public int read(@NotNull byte[] b) throws IOException { + public int read(byte[] b) throws IOException { return read(b, 0, b.length); } @Override - public int read(@NotNull byte[] b, int off, int len) throws IOException { + public int read(byte[] b, int off, int len) throws IOException { int result = super.read(b, off, len); if (collecting && result != -1) { collectedBytes.write(b, off, result); diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java index 54dbe3c3..634ec0b0 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/CollectingOutputStream.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb.hashedblock; -import org.jetbrains.annotations.NotNull; - import java.io.*; /** @@ -61,12 +59,12 @@ public void write(int toWrite) throws IOException { } @Override - public void write(byte @NotNull [] b) throws IOException { + public void write(byte[] b) throws IOException { write(b, 0, b.length); } @Override - public void write(byte @NotNull [] b, int off, int len) throws IOException { + public void write(byte[] b, int off, int len) throws IOException { super.write(b, off, len); } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java index 6d17d5c4..22379b96 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockInputStream.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb.hashedblock; -import org.jetbrains.annotations.NotNull; - import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; @@ -86,7 +84,7 @@ public HashedBlockInputStream(InputStream inputStream, boolean littleEndian) { } @Override - public int read(@NotNull byte[] b, int offset, int length) throws IOException { + public int read(byte[] b, int offset, int length) throws IOException { return get(b, offset, length); } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java index 5fa2e6e6..4367191a 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HashedBlockOutputStream.java @@ -17,8 +17,6 @@ package org.linguafranca.pwdb.hashedblock; -import org.jetbrains.annotations.NotNull; - import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; @@ -92,7 +90,7 @@ public void write(int i) throws IOException { } @Override - public void write(@NotNull byte[] b, int offset, int count) throws IOException { + public void write(byte[] b, int offset, int count) throws IOException { put(b, offset, count); } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java index 7e0fbaca..15294d5f 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java @@ -23,7 +23,6 @@ import java.io.*; import java.nio.ByteOrder; import javax.crypto.Mac; -import org.jetbrains.annotations.NotNull; import com.google.common.io.LittleEndianDataOutputStream; import org.linguafranca.pwdb.format.Helpers; @@ -157,7 +156,7 @@ public void write(int i) throws IOException { } @Override - public void write(byte @NotNull [] b, int offset, int count) throws IOException { + public void write(byte[] b, int offset, int count) throws IOException { put(b, offset, count); } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java new file mode 100644 index 00000000..48a28928 --- /dev/null +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +/** + * This package contains the interfaces that define the org.linguafranca Database API. + *

+ * The API is designed to be a simple, low-level, read-write API for password databases. + *

+ * Class instances of the interfaces are contained in the sub-packages of this package. + */ +@NullMarked +package org.linguafranca.pwdb.hashedblock; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/pom.xml b/pom.xml index f9fcf442..c2710bd3 100644 --- a/pom.xml +++ b/pom.xml @@ -187,11 +187,19 @@ + + org.jspecify + jspecify + 1.0.0 + provided + + org.junit.platform junit-platform-launcher @@ -216,11 +224,19 @@ + + org.jspecify + jspecify + 1.0.0 + provided + + com.google.guava guava From 03a555ece1dcb90bf30aec7816ea66740f8977a0 Mon Sep 17 00:00:00 2001 From: sergmain Date: Fri, 8 May 2026 09:42:07 -0700 Subject: [PATCH 61/66] guava eliminated --- database/pom.xml | 7 + .../pwdb/security/VariantDictionary.java | 18 +-- .../pwdb/test/BinaryPropertiesTest.java | 10 +- .../linguafranca/pwdb/kdbx/ChooseFile.java | 3 +- .../linguafranca/pwdb/kdb/KdbCredentials.java | 4 +- .../linguafranca/pwdb/kdb/KdbSerializer.java | 6 +- .../pwdb/kdbx/jackson/package-info.java | 8 - .../pwdb/kdbx/jackson/util/Util.java | 14 +- .../kdbx/database/validation/Issue27Test.java | 8 +- kdbx-io/pom.xml | 5 + .../org/linguafranca/pwdb/format/Helpers.java | 4 +- .../pwdb/format/KdbxSerializer.java | 18 +-- .../pwdb/format/package-info.java | 7 - .../hashedblock/HmacBlockInputStream.java | 4 +- .../hashedblock/HmacBlockOutputStream.java | 4 +- .../pwdb/hashedblock/package-info.java | 8 - .../pwdb/utils/SwappedDataOutputStream.java | 153 ++++++++++++++++++ .../pwdb/kdbx/KdbxHeaderTest.java | 17 +- .../pwdb/kdbx/KdbxKeyFileTest.java | 27 +++- .../pwdb/kdbx/KdbxSerializerTest.java | 8 +- .../linguafranca/pwdb/kdbx/package-info.java | 22 +++ pom.xml | 15 +- 22 files changed, 281 insertions(+), 89 deletions(-) create mode 100644 kdbx-io/src/main/java/org/linguafranca/pwdb/utils/SwappedDataOutputStream.java create mode 100644 kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/package-info.java diff --git a/database/pom.xml b/database/pom.xml index 9992eabc..6f9401e5 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -31,14 +31,21 @@ Contains root definitions of PWDB Databases etc. + org.bouncycastle bcpkix-jdk18on + + + commons-io + commons-io + com.google.code.findbugs jsr305 diff --git a/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java b/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java index 11b0580c..1738fb3b 100644 --- a/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java +++ b/database/src/main/java/org/linguafranca/pwdb/security/VariantDictionary.java @@ -26,7 +26,7 @@ import java.util.Map; import java.util.UUID; -import static com.google.common.base.Preconditions.checkNotNull; +import static java.util.Objects.requireNonNull; import static org.linguafranca.pwdb.security.VariantDictionary.EntryType.*; /** @@ -87,7 +87,7 @@ public Entry(EntryType entryType, byte[] value) { public Entry(EntryType entryType, byte[] value, ByteOrder byteOrder) { this.type = entryType.value; - this.value = checkNotNull(value, vnn); + this.value = requireNonNull(value, vnn); this.byteOrder = byteOrder; } @@ -189,7 +189,7 @@ public Entry mustGet(String key) { * @param value a buffer containing an appropriate entry */ public void put(String key, EntryType type, byte[] value) { - entries.put(checkNotNull(key), new Entry(type, checkNotNull(value))); + entries.put(requireNonNull(key), new Entry(type, requireNonNull(value))); } /** @@ -200,14 +200,14 @@ public void putUuid(String key, UUID uuid) { ByteBuffer bb = ByteBuffer.wrap(buf); bb.putLong(0, uuid.getMostSignificantBits()); bb.putLong(8, uuid.getLeastSignificantBits()); - entries.put(checkNotNull(key, knn), new Entry(ARRAY, buf)); + entries.put(requireNonNull(key, knn), new Entry(ARRAY, buf)); } /** * Put a byte array under the key defined */ public void putByteArray(String key, byte[] value) { - entries.put(checkNotNull(key, knn), new Entry(ARRAY, value)); + entries.put(requireNonNull(key, knn), new Entry(ARRAY, value)); } /** @@ -217,7 +217,7 @@ public void putLong(String key, long value) { byte[] buf = new byte[8]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putLong(value); - entries.put(checkNotNull(key, knn), new Entry(INT64, buf)); + entries.put(requireNonNull(key, knn), new Entry(INT64, buf)); } /** * Put a long as an unsigned64 under the key defined @@ -226,19 +226,19 @@ public void putULong(String key, long value) { byte[] buf = new byte[8]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putLong(value); - entries.put(checkNotNull(key, knn), new Entry(UINT64, buf)); + entries.put(requireNonNull(key, knn), new Entry(UINT64, buf)); } public void putInt(String key, int value) { byte[] buf = new byte[4]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putInt(value); - entries.put(checkNotNull(key, knn), new Entry(INT32, buf)); + entries.put(requireNonNull(key, knn), new Entry(INT32, buf)); } public void putUInt(String key, int value) { byte[] buf = new byte[4]; ByteBuffer bb = ByteBuffer.wrap(buf).order(ByteOrder.LITTLE_ENDIAN); bb.putInt(value); - entries.put(checkNotNull(key, knn), new Entry(UINT32, buf)); + entries.put(requireNonNull(key, knn), new Entry(UINT32, buf)); } } diff --git a/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java index 4e676ca8..890db5de 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.test; -import com.google.common.io.ByteStreams; +import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; @@ -59,7 +59,7 @@ default void getBinaryProperty() throws Exception { byte[] letterJ = entry.getBinaryProperty("letter J.jpeg"); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); assert testFile != null; - byte[] original = ByteStreams.toByteArray(testFile); + byte[] original = IOUtils.toByteArray(testFile); assertArrayEquals(original, letterJ); } @@ -72,7 +72,7 @@ default void getAnotherBinaryProperty() throws Exception { byte[] letterL = entry.getBinaryProperty("letter L.jpeg"); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); assert testFile != null; - byte[] original = ByteStreams.toByteArray(testFile); + byte[] original = IOUtils.toByteArray(testFile); assertArrayEquals(original, letterL); } @@ -83,7 +83,7 @@ default void getAnotherBinaryProperty() throws Exception { default void setBinaryProperty() throws Exception { InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); assert testFile != null; - byte[] original = ByteStreams.toByteArray(testFile); + byte[] original = IOUtils.toByteArray(testFile); Entry entry = getDatabase().findEntries("Test attachment").get(0); entry.setBinaryProperty("letter L.jpeg", original); byte[] letterL = entry.getBinaryProperty("letter L.jpeg"); @@ -160,7 +160,7 @@ default void createAndSaveCheck() throws IOException { database1.getRootGroup().addEntry(entry); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); assert testFile != null; - byte[] letterJ = ByteStreams.toByteArray(testFile); + byte[] letterJ = IOUtils.toByteArray(testFile); entry.setBinaryProperty("letter J.jpeg", letterJ); saveDatabase(database1, getCredentials("123".getBytes()), Files.newOutputStream(file)); diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java index 65e1eee9..3eb9a6e4 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/ChooseFile.java @@ -17,7 +17,6 @@ package org.linguafranca.pwdb.kdbx; -import com.google.common.base.Strings; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -81,7 +80,7 @@ public String getDescription() { null, null, "123"); - if (Strings.isNullOrEmpty(s)) { + if (s==null || s.isEmpty()) { return; } logger.info("Opening {}", fc.getSelectedFile().getPath()); diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java index ce3326f4..1e86f5c6 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbCredentials.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdb; -import com.google.common.io.ByteStreams; +import org.apache.commons.io.IOUtils; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.security.Encryption; import org.bouncycastle.util.encoders.Hex; @@ -64,7 +64,7 @@ public KeyFile(byte[] password, InputStream inputStream) { md.update(pwKey); try { - byte [] keyFileData = ByteStreams.toByteArray(inputStream); + byte [] keyFileData = IOUtils.toByteArray(inputStream); if (keyFileData.length == 64) { keyFileData = Hex.decode(keyFileData); key = md.digest(keyFileData); diff --git a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java index baf88861..a62207fa 100644 --- a/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java +++ b/kdb/src/main/java/org/linguafranca/pwdb/kdb/KdbSerializer.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdb; -import com.google.common.io.LittleEndianDataInputStream; +import org.apache.commons.io.input.SwappedDataInputStream; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.security.Encryption; @@ -66,7 +66,7 @@ private KdbSerializer() { */ public static KdbDatabase createKdbDatabase(Credentials credentials, KdbHeader kdbHeader, InputStream inputStream) throws IOException { // everything is little endian - DataInput dataInput = new LittleEndianDataInputStream(inputStream); + DataInput dataInput = new SwappedDataInputStream(inputStream); // check the magic values to verify file type checkSignature(dataInput); // load the header @@ -80,7 +80,7 @@ public static KdbDatabase createKdbDatabase(Credentials credentials, KdbHeader k DigestInputStream digestInputStream = new DigestInputStream(decryptedInputStream, digest); // Start the dataInput at wherever we have got to in the stream - dataInput = new LittleEndianDataInputStream(digestInputStream); + dataInput = new SwappedDataInputStream(digestInputStream); // read the decrypted serialized form of all groups KdbDatabase kdbDatabase = new KdbDatabase(); diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java index 6fdcf327..fd08f4c3 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/package-info.java @@ -14,14 +14,6 @@ * limitations under the License. * */ - -/** - * This package contains the interfaces that define the org.linguafranca Database API. - *

- * The API is designed to be a simple, low-level, read-write API for password databases. - *

- * Class instances of the interfaces are contained in the sub-packages of this package. - */ @NullMarked package org.linguafranca.pwdb.kdbx.jackson; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java index 02cc9757..0bb4c68a 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx.jackson.util; -import com.google.common.io.CharStreams; +import org.apache.commons.io.IOUtils; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.StreamFormat; @@ -51,14 +51,17 @@ public static InputStream getDecryptedInputStream (String resourceName, Credenti } public static String streamToString(InputStream inputStream) throws IOException { - return CharStreams.toString(new InputStreamReader(inputStream, StandardCharsets.UTF_8)); + return IOUtils.toString(inputStream, StandardCharsets.UTF_8); } /** * Example shows how to list XML from a database (but not decrypted passwords) */ public static void listDatabase(String resourceName, Credentials creds, OutputStream outputStream) throws IOException { - KdbxDatabase database = KdbxDatabase.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); + KdbxDatabase database; + try (InputStream inputStream = Util.class.getClassLoader().getResourceAsStream(resourceName)) { + database = KdbxDatabase.load(creds, inputStream); + } database.save(new StreamFormat.None(), new KdbxCredentials.None(), outputStream); } @@ -66,7 +69,10 @@ public static void listDatabase(String resourceName, Credentials creds, OutputSt * Example shows how to list XML from a database using specified loader */ public static void listDatabase(DatabaseLoader loader, String resourceName, Credentials creds, OutputStream outputStream) throws IOException { - Database database = loader.load(creds, Util.class.getClassLoader().getResourceAsStream(resourceName)); + Database database; + try (InputStream inputStream = Util.class.getClassLoader().getResourceAsStream(resourceName)) { + database = loader.load(creds, inputStream); + } database.save(new StreamFormat.None(), new KdbxCredentials.None(), outputStream); outputStream.flush(); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java index 6b2ef3d0..565ea703 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java @@ -17,8 +17,7 @@ package org.linguafranca.pwdb.kdbx.database.validation; -import com.google.common.io.CharStreams; -import org.junit.jupiter.api.Disabled; +import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.Helpers; @@ -74,9 +73,10 @@ public void testV4Date() throws IOException { @Test public void testIssue27XML() throws IOException { - InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); + InputStream is = Issue27Test.class.getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); InputStream plainText = KdbxSerializer.createUnencryptedInputStream(creds,new KdbxHeader(), is); - printStream.println(CharStreams.toString(new InputStreamReader(plainText, StandardCharsets.UTF_8))); + String string = IOUtils.toString(plainText, StandardCharsets.UTF_8); + printStream.println(string); } } diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 96444de8..84a78b01 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -40,6 +40,11 @@ commons-codec commons-codec + + + commons-io + commons-io + org.linguafranca.pwdb database diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java index edee8424..1654fb3c 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/Helpers.java @@ -17,9 +17,9 @@ package org.linguafranca.pwdb.format; -import com.google.common.io.ByteStreams; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Hex; +import org.apache.commons.io.IOUtils; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -156,7 +156,7 @@ public static byte[] unzipBinaryContent(byte[] content) { ByteArrayInputStream bais = new ByteArrayInputStream(content); try { GZIPInputStream g = new GZIPInputStream(bais); - return ByteStreams.toByteArray(g); + return IOUtils.toByteArray(g); } catch (IOException e) { throw new IllegalStateException(e); } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java index 49ce6fea..f3acccb7 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/KdbxSerializer.java @@ -17,12 +17,12 @@ package org.linguafranca.pwdb.format; -import com.google.common.io.LittleEndianDataInputStream; -import com.google.common.io.LittleEndianDataOutputStream; +import org.apache.commons.io.input.SwappedDataInputStream; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.hashedblock.*; import org.linguafranca.pwdb.security.Encryption; import org.linguafranca.pwdb.security.VariantDictionary; +import org.linguafranca.pwdb.utils.SwappedDataOutputStream; import javax.crypto.Mac; import java.io.*; @@ -180,7 +180,7 @@ public static OutputStream createEncryptedOutputStream(Credentials credentials, * @throws IOException if the stream cannot be read, etc. */ private static void checkStartBytes(KdbxHeader kdbxHeader, InputStream decryptedInputStream) throws IOException { - LittleEndianDataInputStream ledis = new LittleEndianDataInputStream(decryptedInputStream); + SwappedDataInputStream ledis = new SwappedDataInputStream(decryptedInputStream); byte [] startBytes = new byte[32]; ledis.readFully(startBytes); @@ -196,7 +196,7 @@ private static void checkStartBytes(KdbxHeader kdbxHeader, InputStream decrypted * @throws IOException if the stream cannot be written, etc. */ private static void writeStartBytes(KdbxHeader kdbxHeader, OutputStream encryptedOutputStream) throws IOException { - LittleEndianDataOutputStream ledos = new LittleEndianDataOutputStream(encryptedOutputStream); + SwappedDataOutputStream ledos = new SwappedDataOutputStream(encryptedOutputStream); ledos.write(kdbxHeader.getStreamStartBytes()); } @@ -237,7 +237,7 @@ public static KdbxHeader readOuterHeader(InputStream inputStream, KdbxHeader kdb // HMac calculation depends on having collected a couple of the header fields CollectingInputStream collectingInputStream = new CollectingInputStream(shaDigestInputStream, true); // make values available from LittleEndian - LittleEndianDataInputStream ledis = new LittleEndianDataInputStream(collectingInputStream); + SwappedDataInputStream ledis = new SwappedDataInputStream(collectingInputStream); // file starts with magic number if (!verifyMagicNumber(ledis)) { @@ -382,7 +382,7 @@ public static void writeKdbxHeader(KdbxHeader kdbxHeader, OutputStream outputStr MessageDigest messageDigest = Encryption.getSha256MessageDigestInstance(); DigestOutputStream digestOutputStream = new DigestOutputStream(outputStream, messageDigest); CollectingOutputStream collectingOutputStream = new CollectingOutputStream(digestOutputStream); - LittleEndianDataOutputStream ledos = new LittleEndianDataOutputStream(collectingOutputStream); + SwappedDataOutputStream ledos = new SwappedDataOutputStream(collectingOutputStream); // lengths are short in v3 int in v4 IntConsumer lengthWriter = (i -> { @@ -483,7 +483,7 @@ private static class InnerHeaderType { * @throws IOException on error */ private static void readInnerHeader(KdbxHeader kdbxHeader, InputStream plainTextStream) throws IOException { - DataInput input = new LittleEndianDataInputStream(plainTextStream); + DataInput input = new SwappedDataInputStream(plainTextStream); byte headerType; do { @@ -517,7 +517,7 @@ private static void readInnerHeader(KdbxHeader kdbxHeader, InputStream plainText } public static void writeInnerHeader(KdbxHeader kdbxHeader, OutputStream outputStream) throws IOException { - DataOutput output = new LittleEndianDataOutputStream(outputStream); + DataOutput output = new SwappedDataOutputStream(outputStream); output.writeByte(InnerHeaderType.INNER_RANDOM_STREAM_ID); output.writeInt(4); @@ -602,7 +602,7 @@ public static byte[] serializeVariantDictionary(VariantDictionary v) { * @return true if it looks like this is a kdbx file * @throws IOException on error */ - private static boolean verifyMagicNumber(LittleEndianDataInputStream ledis) throws IOException { + private static boolean verifyMagicNumber(SwappedDataInputStream ledis) throws IOException { int sig1 = ledis.readInt(); int sig2 = ledis.readInt(); return sig1 == SIG1 && sig2 == SIG2; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java index ec08a0a2..fd08e284 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/format/package-info.java @@ -15,13 +15,6 @@ * */ -/** - * This package contains the interfaces that define the org.linguafranca Database API. - *

- * The API is designed to be a simple, low-level, read-write API for password databases. - *

- * Class instances of the interfaces are contained in the sub-packages of this package. - */ @NullMarked package org.linguafranca.pwdb.format; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java index f4d22fad..de0a0bdf 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockInputStream.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.hashedblock; -import com.google.common.io.LittleEndianDataInputStream; +import org.apache.commons.io.input.SwappedDataInputStream; import org.linguafranca.pwdb.format.Helpers; import javax.crypto.Mac; @@ -78,7 +78,7 @@ public HmacBlockInputStream(byte[] key, InputStream inputStream, boolean littleE this.byteOrder = littleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; this.key = key; if (littleEndian) { - input = new LittleEndianDataInputStream(in); + input = new SwappedDataInputStream(in); } else { input = new DataInputStream(in); } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java index 15294d5f..7051644c 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/HmacBlockOutputStream.java @@ -23,8 +23,8 @@ import java.io.*; import java.nio.ByteOrder; import javax.crypto.Mac; -import com.google.common.io.LittleEndianDataOutputStream; import org.linguafranca.pwdb.format.Helpers; +import org.linguafranca.pwdb.utils.SwappedDataOutputStream; /** * Takes a stream of (encrypted GZIPped) data and formats it as HMAC Hashed Blocks to the @@ -76,7 +76,7 @@ public HmacBlockOutputStream(byte [] key, OutputStream outputStream, boolean lit this.key = key; this.byteOrder = littleEndian ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN; if (this.byteOrder.equals(ByteOrder.LITTLE_ENDIAN)) { - this.output = new LittleEndianDataOutputStream(out); + this.output = new SwappedDataOutputStream(out); } else { this.output = new DataOutputStream(out); } diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java index 48a28928..ddf4ea51 100644 --- a/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/hashedblock/package-info.java @@ -14,14 +14,6 @@ * limitations under the License. * */ - -/** - * This package contains the interfaces that define the org.linguafranca Database API. - *

- * The API is designed to be a simple, low-level, read-write API for password databases. - *

- * Class instances of the interfaces are contained in the sub-packages of this package. - */ @NullMarked package org.linguafranca.pwdb.hashedblock; diff --git a/kdbx-io/src/main/java/org/linguafranca/pwdb/utils/SwappedDataOutputStream.java b/kdbx-io/src/main/java/org/linguafranca/pwdb/utils/SwappedDataOutputStream.java new file mode 100644 index 00000000..8fd4fca6 --- /dev/null +++ b/kdbx-io/src/main/java/org/linguafranca/pwdb/utils/SwappedDataOutputStream.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.utils; + +import org.apache.commons.io.EndianUtils; + +import java.io.*; + +/** + * DataOutput for systems relying on little-endian data formats. When written, values will be changed from big-endian to little-endian + * formats for external usage. + * + * @author github@Siggen + * Date: 5/8/2026 + * Time: 9:34 AM + */ +public final class SwappedDataOutputStream extends FilterOutputStream implements DataOutput { + + /** + * Creates a {@code SwappedDataOutputStream} that wraps the given stream. + * + * @param out the stream to delegate to + */ + public SwappedDataOutputStream(OutputStream out) { + super(new DataOutputStream(out)); + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + + @Override + public void writeBoolean(boolean v) throws IOException { + ((DataOutputStream) out).writeBoolean(v); + } + + @Override + public void writeByte(int v) throws IOException { + ((DataOutputStream) out).writeByte(v); + } + + @Deprecated + @Override + public void writeBytes(String s) throws IOException { + ((DataOutputStream) out).writeBytes(s); + } + + /** + * Writes a char as specified by {@link DataOutputStream#writeChar(int)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeChar(int v) throws IOException { + writeShort(v); + } + + /** + * Writes a {@code String} as specified by {@link DataOutputStream#writeChars(String)}, except + * each character is written using little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeChars(String s) throws IOException { + for (int i = 0; i < s.length(); i++) { + writeChar(s.charAt(i)); + } + } + + /** + * Writes a {@code double} as specified by {@link DataOutputStream#writeDouble(double)}, except + * using little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeDouble(double v) throws IOException { + EndianUtils.writeSwappedDouble(out, v); + } + + /** + * Writes a {@code float} as specified by {@link DataOutputStream#writeFloat(float)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeFloat(float v) throws IOException { + EndianUtils.writeSwappedFloat(out, v); + } + + /** + * Writes an {@code int} as specified by {@link DataOutputStream#writeInt(int)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeInt(int v) throws IOException { + EndianUtils.writeSwappedInteger(out, v); + } + + /** + * Writes a {@code long} as specified by {@link DataOutputStream#writeLong(long)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeLong(long v) throws IOException { + EndianUtils.writeSwappedLong(out, v); + } + + /** + * Writes a {@code short} as specified by {@link DataOutputStream#writeShort(int)}, except using + * little-endian byte order. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void writeShort(int v) throws IOException { + EndianUtils.writeSwappedShort(out, (short) v); + } + + @Override + public void writeUTF(String str) throws IOException { + ((DataOutputStream) out).writeUTF(str); + } + + @Override + public void close() throws IOException { + out.close(); + } +} \ No newline at end of file diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java index 854dffb1..bbaef40b 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java @@ -17,8 +17,8 @@ package org.linguafranca.pwdb.kdbx; -import com.google.common.io.CharStreams; -import com.google.common.io.LittleEndianDataInputStream; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.SwappedDataInputStream; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.format.KdbxCredentials; @@ -28,11 +28,11 @@ import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.PrintStream; import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** @@ -44,19 +44,21 @@ public class KdbxHeaderTest { @Disabled // can be used for detailed HMAC debugging public void getHmacStream() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); + assertNotNull(inputStream); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); printStream.println("Version " + header.getVersion()); KdbxCredentials creds = new KdbxCredentials("123".getBytes()); - assert inputStream != null; - KdbxSerializer.readOuterHeaderVerification(header, creds, new LittleEndianDataInputStream(inputStream)); + KdbxSerializer.readOuterHeaderVerification(header, creds, new SwappedDataInputStream(inputStream)); HmacBlockInputStream hmacBlockInputStream = new HmacBlockInputStream(header.getHmacKey(creds), inputStream, true); - printStream.println(CharStreams.toString(new InputStreamReader(hmacBlockInputStream, StandardCharsets.UTF_8))); + String string = IOUtils.toString(hmacBlockInputStream, StandardCharsets.UTF_8); + printStream.println(string); } // check the correct version @Test public void loadAesAesHeader() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-AES.kdbx"); + assertNotNull(inputStream); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); assertEquals(4, header.getVersion()); } @@ -65,6 +67,7 @@ public void loadAesAesHeader() throws IOException { @Test public void loadAesArgonHeader() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); + assertNotNull(inputStream); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); assertEquals(4, header.getVersion()); } @@ -72,12 +75,14 @@ public void loadAesArgonHeader() throws IOException { @Test public void loadChaChaHeader() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-AES.kdbx"); + assertNotNull(inputStream); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); assertEquals(4, header.getVersion()); } @Test public void loadChaChaArgonHeader() throws IOException { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-Argon2-Attachment.kdbx"); + assertNotNull(inputStream); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); assertEquals(4, header.getVersion()); } diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java index 7d158234..eecc2ba8 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx; -import com.google.common.io.CharStreams; +import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.format.KdbxCredentials; @@ -26,7 +26,6 @@ import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; import java.io.PrintStream; import java.nio.charset.StandardCharsets; @@ -42,15 +41,17 @@ public class KdbxKeyFileTest { static PrintStream printStream = getTestPrintStream(); private static void toConsole(InputStream is) throws IOException { - printStream.println(CharStreams.toString(new InputStreamReader(is, StandardCharsets.UTF_8))); + String string = IOUtils.toString(is, StandardCharsets.UTF_8); + printStream.println(string); } /** * Test that we can load a key file and get a 32 byte base64 encoded value back */ @Test - public void testLoad() throws Exception { + public void testLoad() { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("KeyFileDatabase.key"); + assertNotNull(inputStream); byte[] key = KdbxKeyFile.load(inputStream); assertNotNull(key); assertEquals(32, key.length); @@ -62,6 +63,7 @@ public void testLoad() throws Exception { @Test public void testEmptyPasswordCreds() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPassword.kdbx"); + assertNotNull(inputStream); Credentials credentials = new KdbxCredentials(new byte[0]); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); @@ -73,8 +75,9 @@ public void testEmptyPasswordCreds() throws Exception { @Test public void testEmptyPasswordKeyCreds() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPasswordWithKey.kdbx"); + assertNotNull(inputStream); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("EmptyPasswordWithKey.key"); - assert inputStreamKeyFile != null; + assertNotNull(inputStreamKeyFile); Credentials credentials = new KdbxCredentials(new byte[0], inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); @@ -86,8 +89,9 @@ public void testEmptyPasswordKeyCreds() throws Exception { @Test public void testNoPasswordKeyCreds() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("NoPasswordWithKey.kdbx"); + assertNotNull(inputStream); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("NoPasswordWithKey.key"); - assert inputStreamKeyFile != null; + assertNotNull(inputStreamKeyFile); Credentials credentials = new KdbxCredentials(inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); @@ -99,6 +103,7 @@ public void testNoPasswordKeyCreds() throws Exception { @Test public void testEmptyPassword() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("EmptyPassword.kdbx"); + assertNotNull(inputStream); Credentials credentials = new KdbxCredentials(new byte[0]); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); @@ -111,6 +116,8 @@ public void testEmptyPassword() throws Exception { public void testSignedKeyFile() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.keyx"); + assertNotNull(inputStream); + assertNotNull(inputStreamKeyFile); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); @@ -124,6 +131,8 @@ public void testSignatureFails() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test_wrong_hash" + ".keyx"); + assertNotNull(inputStream); + assertNotNull(inputStreamKeyFile); Throwable throwable = assertThrows(RuntimeException.class, () -> { Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); @@ -138,6 +147,8 @@ public void testSignatureFails() throws Exception { public void testKeyFileRandom() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdb_with_random_file.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("random_file"); + assertNotNull(inputStream); + assertNotNull(inputStreamKeyFile); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); @@ -150,6 +161,8 @@ public void testKeyFileRandom() throws Exception { public void testKeyFileHex64() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_keyfile64.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile64"); + assertNotNull(inputStream); + assertNotNull(inputStreamKeyFile); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); @@ -162,6 +175,8 @@ public void testKeyFileHex64() throws Exception { public void testKeyFile32() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("kdbx_keyfile32.kdbx"); InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile32"); + assertNotNull(inputStream); + assertNotNull(inputStreamKeyFile); Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java index 6d3f6679..24b6eda7 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java @@ -17,7 +17,7 @@ package org.linguafranca.pwdb.kdbx; -import com.google.common.io.CharStreams; +import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.format.KdbxCredentials; @@ -44,9 +44,10 @@ public class KdbxSerializerTest { @Test public void testGetPlainTextInputStream() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); + assertNotNull(inputStream); Credentials credentials = new KdbxCredentials("123".getBytes()); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); - printStream.println(CharStreams.toString(new InputStreamReader(decryptedInputStream, StandardCharsets.UTF_8))); + printStream.println(IOUtils.toString(decryptedInputStream, StandardCharsets.UTF_8)); } /** @@ -55,9 +56,10 @@ public void testGetPlainTextInputStream() throws Exception { @Test public void testGetPlainTextInputStream2() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-AES.kdbx"); + assertNotNull(inputStream); Credentials credentials = new KdbxCredentials("123".getBytes()); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); - printStream.println(CharStreams.toString(new InputStreamReader(decryptedInputStream, StandardCharsets.UTF_8))); + printStream.println(IOUtils.toString(decryptedInputStream, StandardCharsets.UTF_8)); } diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/package-info.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/package-info.java new file mode 100644 index 00000000..37dff8e8 --- /dev/null +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/package-info.java @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +@NullMarked +package org.linguafranca.pwdb.kdbx; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/pom.xml b/pom.xml index c2710bd3..60991e98 100644 --- a/pom.xml +++ b/pom.xml @@ -231,17 +231,12 @@ provided com.google.guava guava 33.5.0-jre +--> org.bouncycastle bcpkix-jdk18on @@ -264,10 +259,16 @@ commons-codec 1.21.0 + + + commons-io + commons-io + 2.21.0 + org.junit junit-bom - 5.12.0 + 5.14.3 pom import From 3f1f3f7ae6ead99e112d5486cc0863dbb71e27ee Mon Sep 17 00:00:00 2001 From: sergmain Date: Fri, 8 May 2026 11:26:49 -0700 Subject: [PATCH 62/66] all unit-tests are @Execution(CONCURRENT) now --- .gitignore | 1 + .../pwdb/basic/BasicDatabaseKdbxTest.java | 35 +-- .../basic/BasicDatabaseSerializerTest.java | 83 ++++--- .../pwdb/basic/BasicDatabaseTest.java | 21 +- .../pwdb/basic/BasicEntryTest.java | 37 ++- .../pwdb/basic/BasicGroupTest.java | 23 +- .../test/resources/junit-platform.properties | 1 + .../linguafranca/pwdb/PropertyValueTest.java | 3 + .../pwdb/test/BinaryPropertiesTest.java | 57 +++-- .../pwdb/test/DatabaseTestBase.java | 43 ++-- .../pwdb/test/GroupsAndEntriesTest.java | 90 ++++--- .../pwdb/test/KdbxFileSaveAndReloadTest.java | 81 +++---- .../pwdb/test/ProtectedPropertyTest.java | 52 ++-- .../pwdb/test/ProtectedPropertyTest2.java | 222 ++++++++++-------- .../pwdb/test/RecycleBinTest.java | 22 +- .../linguafranca/pwdb/test/Test123Test.java | 21 +- .../pwdb/test/TrivialDatabaseTest.java | 94 +++++--- .../test/resources/junit-platform.properties | 1 + .../pwdb/kdb/KdbDatabaseTest.java | 52 ++-- .../test/resources/junit-platform.properties | 1 + .../pwdb/kdbx/database/Kdbx123Test.java | 8 +- .../pwdb/kdbx/database/KdbxBinariesTest.java | 27 +-- .../kdbx/database/KdbxDatabaseLoadTest.java | 42 +++- .../pwdb/kdbx/database/KdbxDatabaseTest.java | 8 +- .../kdbx/database/KdbxSaveAndReloadTest.java | 8 +- .../pwdb/kdbx/database/KdbxTestBase.java | 13 +- .../kdbx/database/validation/Issue27Test.java | 11 +- .../kdbx/database/validation/Issue33Test.java | 46 ++-- .../kdbx/database/validation/Issue38Test.java | 6 +- .../kdbx/database/validation/Issue70Test.java | 8 + .../kdbx/database/validation/Issue87Test.java | 6 +- .../kdbx/database/validation/Issue88Test.java | 5 + .../test/resources/junit-platform.properties | 1 + .../test/resources/junit-platform.properties | 1 + pom.xml | 55 +++-- 35 files changed, 706 insertions(+), 479 deletions(-) create mode 100644 basic/src/test/resources/junit-platform.properties create mode 100644 database/src/test/resources/junit-platform.properties create mode 100644 kdb/src/test/resources/junit-platform.properties create mode 100644 kdbx-database/src/test/resources/junit-platform.properties create mode 100644 kdbx-io/src/test/resources/junit-platform.properties diff --git a/.gitignore b/.gitignore index e918a7f4..3da9b48e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,5 @@ build #.gradle #gradle #build.gradle +Calc*_V3.class diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java index 3db445ea..64e9bbbc 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java @@ -18,6 +18,8 @@ package org.linguafranca.pwdb.basic; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; @@ -26,36 +28,41 @@ import org.linguafranca.pwdb.test.DatabaseTestBase; import java.io.*; +import java.nio.file.Files; import java.nio.file.Path; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; -public class BasicDatabaseKdbxTest extends - DatabaseTestBase { +@Execution(CONCURRENT) +public class BasicDatabaseKdbxTest { - public BasicDatabaseKdbxTest(){ - super(BasicDatabase::new, - (credentials, inputStream) -> - new BasicDatabaseSerializer.Xml().loadNx(inputStream), - (database, credentials, outputStream) -> - new BasicDatabaseSerializer.Xml().saveNx((BasicDatabase) database, outputStream), - (credentials) -> null); - } + private static final Supplier dbFunc = ()-> new DatabaseTestBase(BasicDatabase::new, + (_, inputStream) -> + new BasicDatabaseSerializer.Xml().loadNx(inputStream), + (database, _, outputStream) -> + new BasicDatabaseSerializer.Xml().saveNx((BasicDatabase) database, outputStream), + (_) -> null); @Test - public void testKdbx() throws IOException { - BasicDatabase db = new BasicDatabase(); + public void testKdbx(@TempDir Path tempPath) throws IOException { + DatabaseTestBase databaseTestBase = BasicDatabaseKdbxTest.dbFunc.get(); + Database db = databaseTestBase.database; db.setName("test"); db.getRootGroup().addEntry("test") .addProperty(PASSWORD,"password") .addProperty(URL,"http://example.com") .addProperty(USER_NAME,"user"); - db.save(new KdbxStreamFormat(), new KdbxCredentials("123".getBytes()), new FileOutputStream(Path.of(OUTPUT_DIRECTORY_PATH, "test.kdbx").toFile())); + + Path dbPath = tempPath.resolve("test.kdbx"); + db.save(new KdbxStreamFormat(), new KdbxCredentials("123".getBytes()), Files.newOutputStream(dbPath)); KdbxHeader header = new KdbxHeader(); InputStream unencryptedInputStream = KdbxSerializer.createUnencryptedInputStream(new KdbxCredentials("123".getBytes()), - header, new FileInputStream(Path.of(OUTPUT_DIRECTORY_PATH, "test.kdbx").toFile())); + header, Files.newInputStream(dbPath)); + BasicDatabaseSerializer.Xml xml = new BasicDatabaseSerializer.Xml(header.getInnerStreamEncryptor()); Database db2 = xml.load(unencryptedInputStream); assertEquals(db.getName(), db2.getName()); diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java index 4b647c67..5cfe5ca1 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseSerializerTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,8 +18,9 @@ package org.linguafranca.pwdb.basic; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; import org.linguafranca.pwdb.Visitor; @@ -26,21 +28,20 @@ import java.io.*; import java.nio.file.Files; -import java.nio.file.Paths; +import java.nio.file.Path; import java.util.Arrays; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; +@Execution(CONCURRENT) class BasicDatabaseSerializerTest { - public static final String TEST_OUTPUT_DIR = "testOutput/"; - static BasicDatabase database; - static PrintStream out = getTestPrintStream(); - @BeforeAll - static void setUp() throws IOException { - Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); + static BasicDatabase setUpBasicDatabase() { + + BasicDatabase database; database = new BasicDatabase(); database.setName("My Database"); database.setDescription("A database of passwords"); @@ -54,51 +55,61 @@ static void setUp() throws IOException { .addProperty("non-standard", "non-standard value") .addEntry("Entry 3") .setBinaryProperty("gif", new byte[]{1, 2, 3, 4, 5}); + return database; } - Visitor visitor = new Visitor() { - @Override - public void startVisit(Group group) { - out.println("Group: " + group.getName() + " " + group.getUuid()); - assertNotNull(database.findGroup(group.getUuid())); - assertNotNull(group.getDatabase()); - if (!group.isRootGroup()) { - assertNotNull(group.getParent()); + public static Visitor setUpVisitor(BasicDatabase database, PrintStream out) { + Visitor visitor = new Visitor() { + @Override + public void startVisit(Group group) { + out.println("Group: " + group.getName() + " " + group.getUuid()); + assertNotNull(database.findGroup(group.getUuid())); + assertNotNull(group.getDatabase()); + if (!group.isRootGroup()) { + assertNotNull(group.getParent()); + } } - } - @Override - public void endVisit(Group group) { + @Override + public void endVisit(Group group) { - } + } - @Override - public void visit(Entry entry) { - out.println("Entry: " + entry.getTitle() + " " + entry.getUuid()); + @Override + public void visit(Entry entry) { + out.println("Entry: " + entry.getTitle() + " " + entry.getUuid()); for (String propertyName: entry.getPropertyNames()) { out.println(" " + propertyName + ": " + entry.getPropertyValue(propertyName).getValueAsString()); } - for (String propertyName: entry.getBinaryPropertyNames()) { - out.println(" " + propertyName + ": " + Arrays.toString(entry.getBinaryProperty(propertyName))); - } + for (String propertyName: entry.getBinaryPropertyNames()) { + out.println(" " + propertyName + ": " + Arrays.toString(entry.getBinaryProperty(propertyName))); + } Entry entry1 = database.findEntry(entry.getUuid()); assertNotNull(entry1, "Entry not found " + entry.getUuid()); assertEquals(entry.getPropertyNames().size(), entry1.getPropertyNames().size()); assertNotNull(entry.getDatabase()); assertNotNull(entry.getParent()); - } + } - @Override - public boolean isEntriesFirst() { - return false; - } - }; + @Override + public boolean isEntriesFirst() { + return false; + } + }; + return visitor; + } @Test - void saveAndLoad() throws IOException { - new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).save(database, new FileOutputStream(TEST_OUTPUT_DIR + "test.xml")); - BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).load(new FileInputStream(TEST_OUTPUT_DIR + "test.xml")); - loadedDatabase.visit(visitor); + void saveAndLoad(@TempDir Path tempPath) throws IOException { + PrintStream out = getTestPrintStream(); + + BasicDatabase basicDatabase = setUpBasicDatabase(); + OutputStream outputStream = Files.newOutputStream(tempPath.resolve("test.xml")); + new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).save(basicDatabase, outputStream); + + InputStream inputStream = Files.newInputStream(tempPath.resolve( "test.xml")); + BasicDatabase loadedDatabase = new BasicDatabaseSerializer.Xml(new StreamEncryptor.Salsa20(new byte[0])).load(inputStream); + loadedDatabase.visit(setUpVisitor(basicDatabase, out)); out.println(loadedDatabase.getRootGroup().getDatabase().getName()); } } \ No newline at end of file diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java index 156cb343..61c3ca01 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseTest.java @@ -17,11 +17,15 @@ package org.linguafranca.pwdb.basic; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.test.*; +import java.util.function.Supplier; + +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; + +@Execution(CONCURRENT) public class BasicDatabaseTest - extends - DatabaseTestBase implements TrivialDatabaseTest, GroupsAndEntriesTest, @@ -29,12 +33,15 @@ public class BasicDatabaseTest ProtectedPropertyTest, ProtectedPropertyTest2 { - BasicDatabaseTest() { - super(BasicDatabase::new, - (credentials, inputStream) -> + private static final Supplier dbFunc = ()-> new DatabaseTestBase(BasicDatabase::new, + (_, inputStream) -> new BasicDatabaseSerializer.Xml().loadNx(inputStream), - (database, credentials, outputStream) -> + (database, _, outputStream) -> new BasicDatabaseSerializer.Xml().saveNx((BasicDatabase) database, outputStream), - (credentials) -> null); + (_) -> null); + + @Override + public Supplier getDbFunc() { + return dbFunc; } } diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java index a93bf731..d0b923cb 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicEntryTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import java.time.Instant; import java.util.Date; @@ -26,30 +27,31 @@ import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; +@Execution(CONCURRENT) class BasicEntryTest { - private BasicEntry entry; - - @BeforeEach - void setUp() { - entry = new BasicEntry(new BasicDatabase()); - } - @Test void testGetProperty() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + entry.setProperty("name", "value"); assertEquals("value", entry.getProperty("name")); } @Test void testSetProperty() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + entry.setProperty("name", "value"); assertEquals("value", entry.getProperty("name")); } @Test void testRemoveProperty() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + entry.setProperty("name", "value"); assertTrue(entry.removeProperty("name")); assertNull(entry.getProperty("name")); @@ -57,6 +59,8 @@ void testRemoveProperty() { @Test void testGetPropertyNames() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + entry.setProperty("name1", "value1"); entry.setProperty("name2", "value2"); List propertyNames = entry.getPropertyNames(); @@ -66,6 +70,8 @@ void testGetPropertyNames() { @Test void testGetBinaryProperty() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + byte[] data = {1, 2, 3}; entry.setBinaryProperty("binary", data); assertArrayEquals(data, entry.getBinaryProperty("binary")); @@ -73,6 +79,8 @@ void testGetBinaryProperty() { @Test void testSetBinaryProperty() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + byte[] data = {1, 2, 3}; entry.setBinaryProperty("binary", data); assertArrayEquals(data, entry.getBinaryProperty("binary")); @@ -80,6 +88,8 @@ void testSetBinaryProperty() { @Test void testRemoveBinaryProperty() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + byte[] data = {1, 2, 3}; entry.setBinaryProperty("binary", data); assertTrue(entry.removeBinaryProperty("binary")); @@ -88,6 +98,8 @@ void testRemoveBinaryProperty() { @Test void testGetBinaryPropertyNames() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); + byte[] data1 = {1, 2, 3}; byte[] data2 = {4, 5, 6}; entry.setBinaryProperty("binary1", data1); @@ -99,56 +111,66 @@ void testGetBinaryPropertyNames() { @Test void testGetParent() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); assertNull(entry.getParent()); } @Test void testGetUuid() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); UUID uuid = entry.getUuid(); assertNotNull(uuid); } @Test void testGetIcon() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); assertNotNull(entry.getIcon()); } @Test void testSetIcon() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); entry.setIcon(new BasicIcon(1) {}); assertEquals(1, entry.getIcon().getIndex()); } @Test void testGetLastAccessTime() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); Date lastAccessTime = entry.getLastAccessTime(); assertNotNull(lastAccessTime); } @Test void testGetCreationTime() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); Date creationTime = entry.getCreationTime(); assertNotNull(creationTime); } @Test void testGetExpires() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); assertFalse(entry.getExpires()); } @Test void testSetExpires() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); entry.setExpires(true); assertTrue(entry.getExpires()); } @Test void testGetExpiryTime() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); assertNull(entry.getExpiryTime()); } @Test void testSetExpiryTime() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); Date expiryTime = Date.from(Instant.now()); entry.setExpiryTime(expiryTime); assertEquals(expiryTime, entry.getExpiryTime()); @@ -156,6 +178,7 @@ void testSetExpiryTime() { @Test void testGetLastModificationTime() { + BasicEntry entry= new BasicEntry(new BasicDatabase()); assertEquals(entry.getCreationTime(), entry.getLastModificationTime()); } } diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java index bf5e3262..9882b169 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicGroupTest.java @@ -17,8 +17,8 @@ package org.linguafranca.pwdb.basic; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; @@ -26,30 +26,28 @@ import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; +@Execution(CONCURRENT) class BasicGroupTest { - private BasicGroup group; - - @BeforeEach - void setUp() { - group = new BasicGroup(new BasicDatabase()); - } - @Test void testGetName() { + BasicGroup group = new BasicGroup(new BasicDatabase()); group.setName("Test Group"); assertEquals("Test Group", group.getName()); } @Test void testSetName() { + BasicGroup group = new BasicGroup(new BasicDatabase()); group.setName("Test Group"); assertEquals("Test Group", group.getName()); } @Test void testGetEntries() { + BasicGroup group = new BasicGroup(new BasicDatabase()); Entry entry = new BasicEntry(new BasicDatabase()); group.addEntry(entry); List entries = group.getEntries(); @@ -58,6 +56,7 @@ void testGetEntries() { @Test void testAddEntry() { + BasicGroup group = new BasicGroup(new BasicDatabase()); Entry entry = new BasicEntry(new BasicDatabase()); group.addEntry(entry); assertTrue(group.getEntries().contains(entry)); @@ -65,6 +64,7 @@ void testAddEntry() { @Test void testRemoveEntry() { + BasicGroup group = new BasicGroup(new BasicDatabase()); Entry entry = new BasicEntry(new BasicDatabase()); group.addEntry(entry); group.removeEntry(entry); @@ -73,6 +73,7 @@ void testRemoveEntry() { @Test void testGetGroups() { + BasicGroup group = new BasicGroup(new BasicDatabase()); Group subGroup = new BasicGroup(new BasicDatabase()); group.addGroup(subGroup); List groups = group.getGroups(); @@ -81,6 +82,7 @@ void testGetGroups() { @Test void testAddGroup() { + BasicGroup group = new BasicGroup(new BasicDatabase()); Group subGroup = new BasicGroup(new BasicDatabase()); group.addGroup(subGroup); assertTrue(group.getGroups().contains(subGroup)); @@ -88,6 +90,7 @@ void testAddGroup() { @Test void testRemoveGroup() { + BasicGroup group = new BasicGroup(new BasicDatabase()); Group subGroup = new BasicGroup(new BasicDatabase()); group.addGroup(subGroup); assertTrue(group.getGroups().contains(subGroup)); @@ -101,22 +104,26 @@ void testRemoveGroup() { @Test void testGetParent() { + BasicGroup group = new BasicGroup(new BasicDatabase()); assertNull(group.getParent()); } @Test void testGetUuid() { + BasicGroup group = new BasicGroup(new BasicDatabase()); UUID uuid = group.getUuid(); assertNotNull(uuid); } @Test void testGetIcon() { + BasicGroup group = new BasicGroup(new BasicDatabase()); assertEquals(0, group.getIcon().getIndex()); } @Test void testSetIcon() { + BasicGroup group = new BasicGroup(new BasicDatabase()); group.setIcon(new BasicIcon(1)); assertEquals(1, group.getIcon().getIndex()); } diff --git a/basic/src/test/resources/junit-platform.properties b/basic/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..2115a2f1 --- /dev/null +++ b/basic/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled = true diff --git a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java index 070eb210..1ba29d4f 100644 --- a/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/PropertyValueTest.java @@ -20,13 +20,16 @@ import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import java.nio.charset.StandardCharsets; import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.pwdb.PropertyValueUtil.genericTest; +@Execution(CONCURRENT) public class PropertyValueTest { public static final String ANOTHER_SECRET = "password with accents àéç"; diff --git a/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java index 890db5de..64972ba6 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java @@ -19,27 +19,21 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; -import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; import java.io.IOException; import java.io.InputStream; -import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.*; public interface BinaryPropertiesTest { - void newDatabase(); - Database getDatabase(); - Database createDatabase(); - void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; - Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; - Credentials getCredentials(byte[] credentials); + Supplier getDbFunc(); /** * Verify that the database correctly reports that its supports binary attachments - override for @@ -47,7 +41,10 @@ public interface BinaryPropertiesTest { */ @Test default void checkSupported() { - assertTrue(getDatabase().supportsBinaryProperties()); + DatabaseTestBase db = getDbFunc().get(); + + Database database = db.getDatabase(); + assertTrue(database.supportsBinaryProperties()); } /** @@ -55,7 +52,9 @@ default void checkSupported() { */ @Test default void getBinaryProperty() throws Exception { - Entry entry = getDatabase().findEntries("Test attachment").get(0); + DatabaseTestBase db = getDbFunc().get(); + + Entry entry = db.getDatabase().findEntries("Test attachment").get(0); byte[] letterJ = entry.getBinaryProperty("letter J.jpeg"); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); assert testFile != null; @@ -68,7 +67,9 @@ default void getBinaryProperty() throws Exception { */ @Test default void getAnotherBinaryProperty() throws Exception { - Entry entry = getDatabase().findEntries("Test 2 attachment").get(0); + DatabaseTestBase db = getDbFunc().get(); + + Entry entry = db.getDatabase().findEntries("Test 2 attachment").get(0); byte[] letterL = entry.getBinaryProperty("letter L.jpeg"); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); assert testFile != null; @@ -81,15 +82,17 @@ default void getAnotherBinaryProperty() throws Exception { */ @Test default void setBinaryProperty() throws Exception { + DatabaseTestBase db = getDbFunc().get(); + InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter L.jpeg"); assert testFile != null; byte[] original = IOUtils.toByteArray(testFile); - Entry entry = getDatabase().findEntries("Test attachment").get(0); + Entry entry = db.getDatabase().findEntries("Test attachment").get(0); entry.setBinaryProperty("letter L.jpeg", original); byte[] letterL = entry.getBinaryProperty("letter L.jpeg"); assertArrayEquals(original, letterL); assertArrayEquals(new String[]{"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); - assertTrue(getDatabase().isDirty()); + assertTrue(db.getDatabase().isDirty()); } /** @@ -97,10 +100,12 @@ default void setBinaryProperty() throws Exception { */ @Test default void getBinaryPropertyNames() { - Entry entry = getDatabase().findEntries("Test attachment").get(0); + DatabaseTestBase db = getDbFunc().get(); + + Entry entry = db.getDatabase().findEntries("Test attachment").get(0); assertArrayEquals(new String[]{"letter J.jpeg"}, entry.getBinaryPropertyNames().toArray()); - entry = getDatabase().findEntries("Test 2 attachment").get(0); + entry = db.getDatabase().findEntries("Test 2 attachment").get(0); assertArrayEquals(new String[]{"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); } @@ -109,9 +114,11 @@ default void getBinaryPropertyNames() { */ @Test default void checkAddChangeRemoveBinaryProperty() { + DatabaseTestBase db = getDbFunc().get(); + byte[] test = new byte[]{0, 1, 2, 3}; byte[] test2 = new byte[]{3, 2, 1, 0}; - Entry entry = getDatabase().findEntries("Test attachment").get(0); + Entry entry = db.getDatabase().findEntries("Test attachment").get(0); assertEquals(1, entry.getBinaryPropertyNames().size()); entry.setBinaryProperty("test", test); assertArrayEquals(test, entry.getBinaryProperty("test")); @@ -132,18 +139,20 @@ default void checkAddChangeRemoveBinaryProperty() { */ @Test default void saveAndReloadCheck() throws IOException { + DatabaseTestBase testBase = getDbFunc().get(); + Path file = Files.createTempFile("test8", "tmp"); - saveDatabase(getDatabase(), getCredentials("123".getBytes()), Files.newOutputStream(file)); + testBase.saveDatabase(testBase.getDatabase(), testBase.getCredentials("123".getBytes()), Files.newOutputStream(file)); - Database db = loadDatabase(getCredentials("123".getBytes()), Files.newInputStream(file)); + Database db = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), Files.newInputStream(file)); Entry newEntry = db.findEntries("Test attachment").get(0); assertArrayEquals(new String[]{"letter J.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); - Entry oldEntry = getDatabase().findEntries("Test attachment").get(0); + Entry oldEntry = testBase.getDatabase().findEntries("Test attachment").get(0); assertArrayEquals(oldEntry.getBinaryProperty("letter J.jpeg"), newEntry.getBinaryProperty("letter J.jpeg")); newEntry = db.findEntries("Test 2 attachment").get(0); assertArrayEquals(new String[]{"letter J.jpeg", "letter L.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); - oldEntry = getDatabase().findEntries("Test 2 attachment").get(0); + oldEntry = testBase.getDatabase().findEntries("Test 2 attachment").get(0); assertArrayEquals(oldEntry.getBinaryProperty("letter J.jpeg"), newEntry.getBinaryProperty("letter J.jpeg")); assertArrayEquals(oldEntry.getBinaryProperty("letter L.jpeg"), newEntry.getBinaryProperty("letter L.jpeg")); @@ -154,17 +163,19 @@ default void saveAndReloadCheck() throws IOException { */ @Test default void createAndSaveCheck() throws IOException { + DatabaseTestBase testBase = getDbFunc().get(); + Path file = Files.createTempFile("keepass", "tmp"); - Database database1 = createDatabase(); + Database database1 = testBase.createDatabase(); Entry entry = database1.newEntry("Test attachment"); database1.getRootGroup().addEntry(entry); InputStream testFile = getClass().getClassLoader().getResourceAsStream("letter J.jpeg"); assert testFile != null; byte[] letterJ = IOUtils.toByteArray(testFile); entry.setBinaryProperty("letter J.jpeg", letterJ); - saveDatabase(database1, getCredentials("123".getBytes()), Files.newOutputStream(file)); + testBase.saveDatabase(database1, testBase.getCredentials("123".getBytes()), Files.newOutputStream(file)); - Database db = loadDatabase(getCredentials("123".getBytes()), Files.newInputStream(file)); + Database db = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), Files.newInputStream(file)); Entry entry1 = db.findEntries("Test attachment").get(0); // just one property assertArrayEquals(new String[]{"letter J.jpeg"}, entry1.getBinaryPropertyNames().toArray()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java b/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java index 713a3b6d..b4ffdc9e 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/DatabaseTestBase.java @@ -16,6 +16,7 @@ */ package org.linguafranca.pwdb.test; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.BeforeAll; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; @@ -29,7 +30,7 @@ import java.util.function.Function; import java.util.function.Supplier; -public class DatabaseTestBase { +public final class DatabaseTestBase { @FunctionalInterface public interface TriConsumer { @@ -46,29 +47,23 @@ default TriConsumer andThen(TriConsumer creator; - BiFunction loader; - TriConsumer saver; - Function credentials; + public Database database; + public final Supplier creator; + public final BiFunction loader; + public final TriConsumer saver; + public final Function credentials; public DatabaseTestBase(Supplier creator, BiFunction loader, TriConsumer saver, - Function credentials) { + Function credentials) { this.creator = creator; this.loader = loader; this.saver = saver; this.credentials = credentials; + this.database = createDatabase(); } + /** * Create a new database */ @@ -76,13 +71,6 @@ public Database createDatabase() { return this.creator.get(); } - /** - * Create a new database for default use in tests - */ - public void newDatabase() { - database = createDatabase(); - } - /** * Get the current default database */ @@ -98,6 +86,17 @@ public Database loadDatabase(Credentials credentials, InputStream inputStream) { return loader.apply(credentials, inputStream); } + public Database loadAndInitDatabase(byte[] credentials, String resourceName) { + this.database = loader.apply(getCredentials(credentials), getClass().getClassLoader().getResourceAsStream(resourceName)); + return this.database; + } + + public Database loadAndInitDatabase(Credentials credentials, InputStream inputStream) { + this.database = loadDatabase(credentials, inputStream); + return this.database; + } + + public void saveDatabase(Database database, Credentials credentials, OutputStream outputStream){ saver.accept(database, credentials, outputStream); } diff --git a/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java index 3e6dcec9..7adee132 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/GroupsAndEntriesTest.java @@ -42,6 +42,7 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.PASSWORD; @@ -50,27 +51,30 @@ * @author Jo */ public interface GroupsAndEntriesTest { - void newDatabase(); - Database getDatabase(); - Database createDatabase(); + + Supplier getDbFunc(); @Test default void testEmptyDatabase() { - assertEquals("Root", getDatabase().getRootGroup().getName()); - assertEquals(0, getDatabase().getRootGroup().getEntries().size()); - assertEquals(0, getDatabase().getRootGroup().getGroups().size()); + DatabaseTestBase db = getDbFunc().get(); + + assertEquals("Root", db.getDatabase().getRootGroup().getName()); + assertEquals(0, db.getDatabase().getRootGroup().getEntries().size()); + assertEquals(0, db.getDatabase().getRootGroup().getGroups().size()); } @Test default void testAddGroup() { - Group g1 = getDatabase().getRootGroup().addGroup(getDatabase().newGroup("group1")); - assertEquals(1, getDatabase().getRootGroup().getGroups().size()); + DatabaseTestBase db = getDbFunc().get(); + + Group g1 = db.getDatabase().getRootGroup().addGroup(db.getDatabase().newGroup("group1")); + assertEquals(1, db.getDatabase().getRootGroup().getGroups().size()); assertEquals("group1", g1.getName()); assertEquals(0, g1.getGroups().size()); assertEquals(0, g1.getEntries().size()); - assertEquals(g1.getParent(), getDatabase().getRootGroup(), "root is not the parent of its child"); + assertEquals(g1.getParent(), db.getDatabase().getRootGroup(), "root is not the parent of its child"); - Group g2 = getDatabase().newGroup(); + Group g2 = db.getDatabase().newGroup(); assertEquals("", g2.getName()); assertNotNull(g2.getUuid()); assertEquals(0, g2.getIcon().getIndex()); @@ -78,34 +82,38 @@ default void testAddGroup() { assertEquals(0, g2.getEntries().size()); // show that the list of groups is a copy - getDatabase().getRootGroup().getGroups().clear(); - assertEquals(1, getDatabase().getRootGroup().getGroups().size()); + db.getDatabase().getRootGroup().getGroups().clear(); + assertEquals(1, db.getDatabase().getRootGroup().getGroups().size()); assertEquals(0, g1.getEntries().size()); } @Test default void testDeleteGroupDetail () { - Group g1 = getDatabase().getRootGroup().addGroup(getDatabase().newGroup("group1")); - List l1 = getDatabase().getRootGroup().findGroups("group1"); + DatabaseTestBase db = getDbFunc().get(); + + Group g1 = db.getDatabase().getRootGroup().addGroup(db.getDatabase().newGroup("group1")); + List l1 = db.getDatabase().getRootGroup().findGroups("group1"); assertEquals(1, l1.size()); Group g2 = l1.get(0); assertEquals(g2, g1); - Group g3 = getDatabase().getRootGroup().removeGroup(g2); + Group g3 = db.getDatabase().getRootGroup().removeGroup(g2); assertEquals(g3, g1); assertNull(g1.getParent()); - assertEquals(0, getDatabase().getRootGroup().getGroups().size()); - assertEquals(0, getDatabase().getRootGroup().findGroups("group1").size()); + assertEquals(0, db.getDatabase().getRootGroup().getGroups().size()); + assertEquals(0, db.getDatabase().getRootGroup().findGroups("group1").size()); } @Test default void testAddRemoveEntry() { - Entry e1 = getDatabase().getRootGroup().addEntry(getDatabase().newEntry()); + DatabaseTestBase db = getDbFunc().get(); + + Entry e1 = db.getDatabase().getRootGroup().addEntry(db.getDatabase().newEntry()); e1.setTitle("entry1"); - List l1 = getDatabase().findEntries("entry1"); + List l1 = db.getDatabase().findEntries("entry1"); assertEquals(1, l1.size()); - Entry e12 = getDatabase().getRootGroup().addEntry(getDatabase().newEntry("entry12")); - List l2 = getDatabase().findEntries("entry1"); + Entry e12 = db.getDatabase().getRootGroup().addEntry(db.getDatabase().newEntry("entry12")); + List l2 = db.getDatabase().findEntries("entry1"); assertEquals(2, l2.size()); // show that the entries are different @@ -113,19 +121,21 @@ default void testAddRemoveEntry() { // show that the list is a copy l2.clear(); - assertEquals(2, getDatabase().findEntries("entry1").size()); + assertEquals(2, db.getDatabase().findEntries("entry1").size()); // show that we get an equivalent entry when we remove to when we inserted - Entry e12b = getDatabase().getRootGroup().removeEntry(e12); + Entry e12b = db.getDatabase().getRootGroup().removeEntry(e12); assertEquals(e12b, e12); // has been unhooked from parent assertNull(e12.getParent()); - assertEquals(1, getDatabase().findEntries("entry1").size()); + assertEquals(1, db.getDatabase().findEntries("entry1").size()); } @Test default void testSetFields () { - Entry e1 = getDatabase().newEntry("Entry 1"); + DatabaseTestBase db = getDbFunc().get(); + + Entry e1 = db.getDatabase().newEntry("Entry 1"); e1.setNotes("this looks a little like Entry 2"); assertEquals("this looks a little like Entry 2", e1.getNotes()); e1.setUsername("jake@window.com"); @@ -140,7 +150,7 @@ default void testSetFields () { assertTrue(e1.matchTitle("1")); assertFalse(e1.matchTitle("doggy")); - Icon ic1 = getDatabase().newIcon(27); + Icon ic1 = db.getDatabase().newIcon(27); e1.setIcon(ic1); assertEquals(e1.getIcon(), ic1); @@ -166,7 +176,7 @@ default void testSetFields () { assertEquals(0, properties.size()); } catch (UnsupportedOperationException e) { // databases don't have to support arbitrary properties - assertFalse(getDatabase().supportsNonStandardPropertyNames()); + assertFalse(db.getDatabase().supportsNonStandardPropertyNames()); assertArrayEquals(e1.getPropertyNames().toArray(), Entry.STANDARD_PROPERTY_NAMES.toArray()); } @@ -179,8 +189,10 @@ default void testSetFields () { @Test default void testTimes() { + DatabaseTestBase db = getDbFunc().get(); + long beforeSecond = Instant.now().toEpochMilli()/1000; - Entry entry = getDatabase().newEntry(); + Entry entry = db.getDatabase().newEntry(); long afterSecond = Instant.now().toEpochMilli()/1000; long createdSecond = entry.getCreationTime().getTime()/1000; @@ -200,13 +212,15 @@ default void testTimes() { @Test default void checkAddChangeRemoveProperty() { + DatabaseTestBase db = getDbFunc().get(); + // only applies to databases that support arbitrary properties - Entry entry = getDatabase().newEntry(); + Entry entry = db.getDatabase().newEntry(); assertEquals(Entry.STANDARD_PROPERTY_NAMES.size(), entry.getPropertyNames().size()); try { entry.setProperty("test", "test1"); } catch (UnsupportedOperationException e) { - if (!getDatabase().supportsNonStandardPropertyNames()) { + if (!db.getDatabase().supportsNonStandardPropertyNames()) { return; } fail("Database must report that it doesn't support non standard properties"); @@ -229,7 +243,9 @@ default void checkAddChangeRemoveProperty() { @Test default void testNewEntryInit() { - Entry e2 = getDatabase().newEntry(); + DatabaseTestBase db = getDbFunc().get(); + + Entry e2 = db.getDatabase().newEntry(); assertNull(e2.getParent()); assertEquals("", e2.getProperty(PASSWORD)); assertNotNull(e2.getUuid()); @@ -245,16 +261,18 @@ default void testNewEntryInit() { @Test default void testCopy() throws IOException { - Entry entry1 = getDatabase().newEntry(); + DatabaseTestBase db = getDbFunc().get(); + + Entry entry1 = db.getDatabase().newEntry(); entry1.setTitle("Entry"); entry1.setUsername("Username"); entry1.setProperty(PASSWORD, "Password"); entry1.setUrl("https://dont.follow.me"); entry1.setNotes("Notes"); - entry1.setIcon(getDatabase().newIcon(2)); + entry1.setIcon(db.getDatabase().newIcon(2)); // create a new Database - Database database2 = createDatabase(); + Database database2 = db.createDatabase(); // create a new Entry in new Database Entry entry2 = database2.newEntry(entry1); @@ -266,9 +284,9 @@ default void testCopy() throws IOException { assertEquals(entry1.getIcon(), entry2.getIcon()); assertNotEquals(entry1.getUuid(), entry2.getUuid()); - Group group1 = getDatabase().newGroup(); + Group group1 = db.getDatabase().newGroup(); group1.setName("Group"); - group1.setIcon(getDatabase().newIcon(3)); + group1.setIcon(db.getDatabase().newIcon(3)); Group group2 = database2.newGroup(group1); assertEquals(group1.getName(), group2.getName()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java index a8b7ab85..f680977a 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,22 +16,7 @@ * */ -package org.linguafranca.pwdb.test;/* - * Copyright 2015 Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - +package org.linguafranca.pwdb.test; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -40,6 +26,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import java.util.UUID; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; @@ -48,18 +35,13 @@ /** * @author jo + * @author Sergio Lissner */ public interface KdbxFileSaveAndReloadTest { - void newDatabase(); - Database getDatabase(); - Database createDatabase(); - void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; - Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; - Credentials getCredentials(byte[] credentials); - boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2); - + Supplier getDbFunc(); + boolean verifyStreamFormat(StreamFormat s1, StreamFormat s2); /** * Test verifies that entries contain the same content on reload as they did on save, @@ -67,6 +49,7 @@ public interface KdbxFileSaveAndReloadTest { */ @Test default void saveAndReloadTest() throws IOException { + DatabaseTestBase testBase = getDbFunc().get(); long now = System.currentTimeMillis(); @@ -76,7 +59,7 @@ default void saveAndReloadTest() throws IOException { //output.save(new StreamFormat.None(), new Credentials.None(), printStream); FileOutputStream fos = new FileOutputStream("testOutput/test1.kdbx"); - saveDatabase(output, getCredentials("123".getBytes()), fos); + testBase.saveDatabase(output, testBase.getCredentials("123".getBytes()), fos); assertFalse(output.isDirty()); fos.flush(); fos.close(); @@ -86,7 +69,7 @@ default void saveAndReloadTest() throws IOException { FileInputStream fis = new FileInputStream("testOutput/test1.kdbx"); - Database input = loadDatabase(getCredentials("123".getBytes()), fis); + Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); verifyContents(input); } @@ -95,8 +78,14 @@ default void saveAndReloadTest() throws IOException { */ @Test default void saveAndReloadTest2() throws IOException { - Database attachment = loadDatabase(getCredentials("123".getBytes()), - getClass().getClassLoader().getResourceAsStream("Attachment.kdbx" )); + DatabaseTestBase testBase = getDbFunc().get(); + + Database attachment; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx")) { + assertNotNull(inputStream); + attachment = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), + inputStream); + } Entry entry = attachment.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg"}, entry.getBinaryPropertyNames().toArray()); @@ -105,16 +94,17 @@ default void saveAndReloadTest2() throws IOException { assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry2.getBinaryPropertyNames().toArray()); byte[] content = entry2.getBinaryProperty("letter L.jpeg"); + assertNotNull(content); entry.setBinaryProperty("letter L.jpeg", content); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); FileOutputStream fos = new FileOutputStream("testOutput/test2.kdbx"); - saveDatabase(attachment, getCredentials("123".getBytes()), fos); + testBase.saveDatabase(attachment, testBase.getCredentials("123".getBytes()), fos); fos.flush(); fos.close(); FileInputStream fis = new FileInputStream("testOutput/test2.kdbx"); - Database input = loadDatabase(getCredentials("123".getBytes()), fis); + Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); entry = input.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); @@ -122,7 +112,7 @@ default void saveAndReloadTest2() throws IOException { //saveDatabase(input, new StreamFormat.None(), new Credentials.None(), printStream); } - String [] testFiles = {"V4-AES-AES.kdbx", + static String [] testFiles = {"V4-AES-AES.kdbx", "V4-AES-Argon2.kdbx", "V4-ChaCha20-AES.kdbx", "V4-ChaCha20-Argon2-Attachment.kdbx"}; @@ -132,22 +122,29 @@ default void saveAndReloadTest2() throws IOException { */ @Test default void saveAndReloadTest3() throws IOException { + DatabaseTestBase testBase = getDbFunc().get(); + for (String resource: testFiles) { - Database database = loadDatabase(this.getCredentials("123".getBytes()), - getClass().getClassLoader().getResourceAsStream(resource)); + Database database; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resource)) { + assertNotNull(inputStream); + database = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), inputStream); + } StreamFormat format1 = database.getStreamFormat(); - database.save(getCredentials("123".getBytes()), Files.newOutputStream(Paths.get("testOutput/test3.kdbx"))); + database.save(testBase.getCredentials("123".getBytes()), Files.newOutputStream(Paths.get("testOutput/test3.kdbx"))); FileInputStream fis = new FileInputStream("testOutput/test3.kdbx"); - Database input = loadDatabase(getCredentials("123".getBytes()), fis); + Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); StreamFormat format2 = input.getStreamFormat(); assertTrue(verifyStreamFormat(format1, format2)); } } private Database createNewDatabase() throws IOException { - Database database = createDatabase(); + DatabaseTestBase testBase = getDbFunc().get(); + + Database database = testBase.createDatabase(); for (@SuppressWarnings("WrapperTypeMayBePrimitive") Integer g = 0; g < 5; g++){ Group group = database.getRootGroup().addGroup(database.newGroup(g.toString())); @@ -159,7 +156,7 @@ private Database createNewDatabase() throws IOException { return database; } - private Entry entryFactory(Database database, String g, int e) { + private static Entry entryFactory(Database database, String g, int e) { Entry result = database.newEntry(); result.addProperty(TITLE, g + "-" + e) .addProperty(USER_NAME, g + " - un - " + e) @@ -169,7 +166,7 @@ private Entry entryFactory(Database database, String g, int e) { return result; } - private void verifyContents(Database database) { + private static void verifyContents(Database database) { for (Integer g = 0; g< 5; g++){ Group group = database.getRootGroup().getGroups().get(g); assertEquals(g.toString(), group.getName()); @@ -195,10 +192,12 @@ private void verifyContents(Database database) { */ @Disabled default void saveNewDatabase () throws IOException { + DatabaseTestBase testBase = getDbFunc().get(); + Database database = createNewDatabase(); FileOutputStream outputStream = new FileOutputStream("compatibility.kdbx"); - saveDatabase(database, getCredentials("123".getBytes()), outputStream); + testBase.saveDatabase(database, testBase.getCredentials("123".getBytes()), outputStream); } /** @@ -216,7 +215,9 @@ default void inspectNewDatabase () throws IOException { // the assertions here somewhat duplicate those elsewhere @Test default void testNewDatabase() throws IOException { - Database database = createDatabase(); + DatabaseTestBase testBase = getDbFunc().get(); + + Database database = testBase.createDatabase(); Group root = database.getRootGroup(); assertTrue(root.isRootGroup()); assertEquals(0, root.getGroups().size()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java index 2783df77..7e7ae22f 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest.java @@ -22,6 +22,7 @@ import org.linguafranca.pwdb.*; import java.util.List; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.PASSWORD; @@ -29,63 +30,68 @@ public interface ProtectedPropertyTest { - void newDatabase(); - - Database getDatabase(); - - @BeforeEach - default void setup() throws Exception { - newDatabase(); - } + Supplier getDbFunc(); @Test default void testShouldProtect() { + DatabaseTestBase testBase = getDbFunc().get(); + String propertyName = PASSWORD; assertThrows(UnsupportedOperationException.class, - () -> getDatabase().setShouldProtect(propertyName, false), + () -> testBase.getDatabase().setShouldProtect(propertyName, false), "By default the ProtectionStrategy should be immutable"); - assertTrue(getDatabase().shouldProtect(propertyName)); + assertTrue(testBase.getDatabase().shouldProtect(propertyName)); } @Test default void testSetShouldProtect() { - getDatabase().setPropertyValueStrategy(new PropertyValue.Strategy.MutableProtectionStrategy()); + DatabaseTestBase testBase = getDbFunc().get(); + + testBase.getDatabase().setPropertyValueStrategy(new PropertyValue.Strategy.MutableProtectionStrategy()); String propertyName = PASSWORD; - getDatabase().setShouldProtect(propertyName, true); - assertTrue(getDatabase().shouldProtect(propertyName)); + testBase.getDatabase().setShouldProtect(propertyName, true); + assertTrue(testBase.getDatabase().shouldProtect(propertyName)); - getDatabase().setShouldProtect(propertyName, false); - assertFalse(getDatabase().shouldProtect(propertyName)); + testBase.getDatabase().setShouldProtect(propertyName, false); + assertFalse(testBase.getDatabase().shouldProtect(propertyName)); } @Test default void testListShouldProtect() { - getDatabase().setPropertyValueStrategy(new PropertyValue.Strategy.MutableProtectionStrategy()); + DatabaseTestBase testBase = getDbFunc().get(); + + testBase.getDatabase().setPropertyValueStrategy(new PropertyValue.Strategy.MutableProtectionStrategy()); String propertyName1 = PASSWORD; String propertyName2 = USER_NAME; - getDatabase().setShouldProtect(propertyName1, true); - getDatabase().setShouldProtect(propertyName2, true); + testBase.getDatabase().setShouldProtect(propertyName1, true); + testBase.getDatabase().setShouldProtect(propertyName2, true); - List protectedProperties = getDatabase().listShouldProtect(); + List protectedProperties = testBase.getDatabase().listShouldProtect(); assertTrue(protectedProperties.contains(propertyName1)); assertTrue(protectedProperties.contains(propertyName2)); } @Test default void testGetPropertyValueStrategy() { - PropertyValue.Strategy strategy = getDatabase().getPropertyValueStrategy(); + DatabaseTestBase testBase = getDbFunc().get(); + + PropertyValue.Strategy strategy = testBase.getDatabase().getPropertyValueStrategy(); assertNotNull(strategy); } @Test default void testSetPropertyValueStrategy() { + DatabaseTestBase testBase = getDbFunc().get(); + PropertyValue.Strategy newStrategy = new PropertyValue.Strategy.Default(); - getDatabase().setPropertyValueStrategy(newStrategy); - assertEquals(newStrategy, getDatabase().getPropertyValueStrategy()); + testBase.getDatabase().setPropertyValueStrategy(newStrategy); + assertEquals(newStrategy, testBase.getDatabase().getPropertyValueStrategy()); } @Test default void testSupportsPropertyValueStrategy() { - assertTrue(getDatabase().supportsPropertyValueStrategy()); + DatabaseTestBase testBase = getDbFunc().get(); + + assertTrue(testBase.getDatabase().supportsPropertyValueStrategy()); } } \ No newline at end of file diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java index 139627e3..26ed2d17 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java @@ -17,9 +17,7 @@ package org.linguafranca.pwdb.test; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.PropertyValue; @@ -27,122 +25,140 @@ import java.io.*; import java.util.List; import java.util.function.Consumer; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.*; public interface ProtectedPropertyTest2 { - - void newDatabase(); - Database getDatabase(); - void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; - Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; - Credentials getCredentials(byte[] credentials); - - @BeforeEach - default void ppt2SetUp() throws Exception { - newDatabase(); + + Supplier getDbFunc(); + + /* + void newDatabase(); + + Database testBase.getDatabase(); + + void saveDatabase(Database database, Credentials credentials, OutputStream outputStream) throws IOException; + + Database loadDatabase(Credentials credentials, InputStream inputStream) throws IOException; + + Credentials getCredentials(byte[] credentials); + + @BeforeEach + default void ppt2SetUp() throws Exception { + newDatabase(); + } + +*/ + /** + * Catches expected thrown exception + * + * @param f a Consumer i.e. a function that does have output + * @param c the exception that is expected to be thrown when the function is executed + */ + private static void catchException(Consumer f, @SuppressWarnings("SameParameterValue") Class c) { + try { + f.accept(null); } - - /** - * Catches expected thrown exception - * - * @param f a Consumer i.e. a function that does have output - * @param c the exception that is expected to be thrown when the function is executed - */ - private void catchException(Consumer f, @SuppressWarnings("SameParameterValue") Class c) { - try { - f.accept(null); - } catch (Exception e) { - if (c.equals(e.getClass())) { - return; - } + catch (Exception e) { + if (c.equals(e.getClass())) { + return; } - fail(c.getName() + " expected"); } + fail(c.getName() + " expected"); + } - /** - * Check that unsupported operations throw exceptions - */ - @Test - default void noPropertyValueSupport() { - if (getDatabase().supportsPropertyValueStrategy()) return; + /** + * Check that unsupported operations throw exceptions + */ + @Test + default void noPropertyValueSupport() { + DatabaseTestBase testBase = getDbFunc().get(); - assertFalse(getDatabase().supportsPropertyValueStrategy()); - // test a sample of the methods that are supposed to thrown if used here - catchException((z) -> getDatabase().setPropertyValueStrategy(null), UnsupportedOperationException.class); - catchException((z) -> getDatabase().getPropertyValueStrategy(), UnsupportedOperationException.class); - catchException((z) -> getDatabase().setShouldProtect(null, true), UnsupportedOperationException.class); - catchException((z) -> getDatabase().listShouldProtect(), UnsupportedOperationException.class); + if (testBase.getDatabase().supportsPropertyValueStrategy()) { + return; + } - Entry entry = getDatabase().newEntry(); - catchException((z) -> entry.getPropertyValue(null), UnsupportedOperationException.class); - catchException((z) -> entry.setPropertyValue(null, (PropertyValue) null), UnsupportedOperationException.class); + assertFalse(testBase.getDatabase().supportsPropertyValueStrategy()); + // test a sample of the methods that are supposed to thrown if used here + catchException((_) -> testBase.getDatabase().setPropertyValueStrategy(null), UnsupportedOperationException.class); + catchException((_) -> testBase.getDatabase().getPropertyValueStrategy(), UnsupportedOperationException.class); + catchException((_) -> testBase.getDatabase().setShouldProtect(null, true), UnsupportedOperationException.class); + catchException((_) -> testBase.getDatabase().listShouldProtect(), UnsupportedOperationException.class); - } + Entry entry = testBase.getDatabase().newEntry(); + catchException((z) -> entry.getPropertyValue(null), UnsupportedOperationException.class); + catchException((z) -> entry.setPropertyValue(null, (PropertyValue) null), UnsupportedOperationException.class); - @Test - default void checkDefaults(){ - // these are the defaults established by createDatabase() - assertTrue(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_PASSWORD)); - assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_USER_NAME)); - assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_NOTES)); - assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_URL)); - assertFalse(getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_TITLE)); - } + } - @Test - default void expectedStorageType() throws IOException { - if (!getDatabase().supportsPropertyValueStrategy()) { - return; - } + @Test + default void checkDefaults() { + DatabaseTestBase testBase = getDbFunc().get(); + + // these are the defaults established by createDatabase() + assertTrue(testBase.getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_PASSWORD)); + assertFalse(testBase.getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_USER_NAME)); + assertFalse(testBase.getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_NOTES)); + assertFalse(testBase.getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_URL)); + assertFalse(testBase.getDatabase().shouldProtect(Entry.STANDARD_PROPERTY_NAME_TITLE)); + } - // Password is protected - PropertyValue.Strategy pvs = getDatabase().getPropertyValueStrategy(); - PropertyValue pv = pvs.getFactoryFor("Password").of("123"); - assertEquals(pv.getClass(), pvs.newProtected().of("a").getClass()); - assertTrue(pv.isProtected()); - - // Notes isn't protected - pv = pvs.getFactoryFor("Notes").of("123"); - assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); - assertFalse(pv.isProtected()); - - // "random" is not protected - pv = pvs.getFactoryFor("random").of("123"); - assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); - assertFalse(pv.isProtected()); - - // create a strategy that makes "random" protected - PropertyValue.Strategy newPvs = new PropertyValue.Strategy.MutableProtectionStrategy(); - getDatabase().setPropertyValueStrategy(newPvs); - getDatabase().setShouldProtect("random", true); - - // get a value for random and check it is protected - pv = newPvs.getFactoryFor("random").of("123"); - assertEquals(pv.getClass(), pvs.newProtected().of("b").getClass()); - assertTrue(pv.isProtected()); - - // create an entry with random property as protected value - Entry entry = getDatabase().newEntry("Test Random"); - entry.setPropertyValue("random", pv); - getDatabase().getRootGroup().addEntry(entry); - - // Save database - - FileOutputStream fos = new FileOutputStream("testOutput/test9.kdbx"); - saveDatabase(getDatabase(), getCredentials("123".getBytes()), fos); - fos.flush(); - fos.close(); - - // reload database, "random" is still protected even though it's not protected by default - FileInputStream fis = new FileInputStream("testOutput/test9.kdbx"); - Database input = loadDatabase(getCredentials("123".getBytes()), fis); - - List entries = input.findEntries("random"); - assertEquals(1, entries.size()); - assertTrue(entries.get(0).getPropertyValue("random").isProtected()); - assertFalse(input.getPropertyValueStrategy().getProtectedProperties().contains("random")); + @Test + default void expectedStorageType() throws IOException { + DatabaseTestBase testBase = getDbFunc().get(); + + if (!testBase.getDatabase().supportsPropertyValueStrategy()) { + return; } + // Password is protected + PropertyValue.Strategy pvs = testBase.getDatabase().getPropertyValueStrategy(); + PropertyValue pv = pvs.getFactoryFor("Password").of("123"); + assertEquals(pv.getClass(), pvs.newProtected().of("a").getClass()); + assertTrue(pv.isProtected()); + + // Notes isn't protected + pv = pvs.getFactoryFor("Notes").of("123"); + assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); + assertFalse(pv.isProtected()); + + // "random" is not protected + pv = pvs.getFactoryFor("random").of("123"); + assertEquals(pv.getClass(), pvs.newUnprotected().of("b").getClass()); + assertFalse(pv.isProtected()); + + // create a strategy that makes "random" protected + PropertyValue.Strategy newPvs = new PropertyValue.Strategy.MutableProtectionStrategy(); + testBase.getDatabase().setPropertyValueStrategy(newPvs); + testBase.getDatabase().setShouldProtect("random", true); + + // get a value for random and check it is protected + pv = newPvs.getFactoryFor("random").of("123"); + assertEquals(pv.getClass(), pvs.newProtected().of("b").getClass()); + assertTrue(pv.isProtected()); + + // create an entry with random property as protected value + Entry entry = testBase.getDatabase().newEntry("Test Random"); + entry.setPropertyValue("random", pv); + testBase.getDatabase().getRootGroup().addEntry(entry); + + // Save database + + FileOutputStream fos = new FileOutputStream("testOutput/test9.kdbx"); + testBase.saveDatabase(testBase.getDatabase(), testBase.getCredentials("123".getBytes()), fos); + fos.flush(); + fos.close(); + + // reload database, "random" is still protected even though it's not protected by default + FileInputStream fis = new FileInputStream("testOutput/test9.kdbx"); + Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); + + List entries = input.findEntries("random"); + assertEquals(1, entries.size()); + assertTrue(entries.get(0).getPropertyValue("random").isProtected()); + assertFalse(input.getPropertyValueStrategy().getProtectedProperties().contains("random")); } +} + diff --git a/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java b/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java index f2b3ed49..3ca4731b 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/RecycleBinTest.java @@ -23,6 +23,8 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Group; +import java.util.function.Supplier; + import static org.junit.jupiter.api.Assertions.*; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -31,18 +33,14 @@ */ public interface RecycleBinTest { - void newDatabase(); - Database getDatabase(); - - @BeforeEach - default void makeDatabase() throws Exception { - newDatabase(); - } + Supplier getDbFunc(); @Test default void recycleBinEntries() { - assumeTrue(getDatabase().supportsRecycleBin(), "Database does not support recycle bin, skipping test"); - Database database = getDatabase(); + DatabaseTestBase testBase = getDbFunc().get(); + + assumeTrue(testBase.getDatabase().supportsRecycleBin(), "Database does not support recycle bin, skipping test"); + Database database = testBase.getDatabase(); database.enableRecycleBin(false); assertFalse(database.isRecycleBinEnabled()); assertNull(database.getRecycleBin()); @@ -75,8 +73,10 @@ default void recycleBinEntries() { @Test default void recycleBinGroups() { - assumeTrue(getDatabase().supportsRecycleBin(), "Database does not support recycle bin, skipping test"); - Database database = getDatabase(); + DatabaseTestBase testBase = getDbFunc().get(); + + assumeTrue(testBase.getDatabase().supportsRecycleBin(), "Database does not support recycle bin, skipping test"); + Database database = testBase.getDatabase(); database.enableRecycleBin(true); assertTrue(database.isRecycleBinEnabled()); Group recycleBin = database.getRecycleBin(); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java index 957d91eb..b77eed1d 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java @@ -23,13 +23,15 @@ import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Visitor; +import java.io.IOException; +import java.io.InputStream; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; +import java.util.function.Supplier; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.PASSWORD; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -42,17 +44,22 @@ */ public interface Test123Test { + Supplier getDbFunc(); + boolean getSkipDateCheck(); - Database loadDatabase(Credentials credentials, java.io.InputStream inputStream); - Credentials getCredentials(byte[] credentials); String getFileName(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ssX"); @Test - default void test123File() throws ParseException { - Database database = loadDatabase(getCredentials("123".getBytes()), - getClass().getClassLoader().getResourceAsStream(getFileName())); + default void test123File() throws ParseException, IOException { + DatabaseTestBase db = getDbFunc().get(); + + Database database; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(getFileName())) { + assertNotNull(inputStream); + database = db.loadDatabase(db.getCredentials("123".getBytes()), inputStream); + } // visit all groups and entries and list them to console database.visit(new Visitor.Print(getTestPrintStream())); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java index a9288dbb..88caea66 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/TrivialDatabaseTest.java @@ -26,106 +26,122 @@ import org.linguafranca.pwdb.Icon; import java.util.UUID; +import java.util.function.Supplier; import static org.junit.jupiter.api.Assertions.*; -@TestInstance(TestInstance.Lifecycle.PER_CLASS) public interface TrivialDatabaseTest { - void newDatabase(); - Database getDatabase(); - - @BeforeEach - default void setUp() { - newDatabase(); - } + Supplier getDbFunc(); @Test default void testGetRootGroup() { - Group root = getDatabase().getRootGroup(); + DatabaseTestBase db = getDbFunc().get(); + + Group root = db.getDatabase().getRootGroup(); assertNotNull(root); assertEquals("Root", root.getName()); } @Test default void testNewGroup() { - Group group = getDatabase().newGroup(); + DatabaseTestBase db = getDbFunc().get(); + + Group group = db.getDatabase().newGroup(); assertNotNull(group); } @Test default void testNewEntry() { - Entry entry = getDatabase().newEntry(); + DatabaseTestBase db = getDbFunc().get(); + + Entry entry = db.getDatabase().newEntry(); assertNotNull(entry); } @Test default void testNewIcon() { - Icon icon = getDatabase().newIcon(); + DatabaseTestBase db = getDbFunc().get(); + + Icon icon = db.getDatabase().newIcon(); assertNotNull(icon); } @Test default void testNewIconWithInteger() { - Icon icon = getDatabase().newIcon(1); + DatabaseTestBase db = getDbFunc().get(); + + Icon icon = db.getDatabase().newIcon(1); assertNotNull(icon); assertEquals(1, icon.getIndex()); } @Test default void testRecycleBin() { - if (getDatabase().supportsRecycleBin()) { - assertTrue(getDatabase().isRecycleBinEnabled()); - Group recycleBin = getDatabase().getRecycleBin(); + DatabaseTestBase db = getDbFunc().get(); + + if (db.getDatabase().supportsRecycleBin()) { + assertTrue(db.getDatabase().isRecycleBinEnabled()); + Group recycleBin = db.getDatabase().getRecycleBin(); assertNotNull(recycleBin); } else { - assertFalse(getDatabase().isRecycleBinEnabled()); - assertThrows(UnsupportedOperationException.class, () -> getDatabase().enableRecycleBin(true)); - assertNull(getDatabase().getRecycleBin()); + assertFalse(db.getDatabase().isRecycleBinEnabled()); + assertThrows(UnsupportedOperationException.class, () -> db.getDatabase().enableRecycleBin(true)); + assertNull(db.getDatabase().getRecycleBin()); } } @Test default void testGetSetName() { - assertEquals("New Database", getDatabase().getName()); - getDatabase().setName("Test Database"); - assertEquals("Test Database", getDatabase().getName()); + DatabaseTestBase db = getDbFunc().get(); + + assertEquals("New Database", db.getDatabase().getName()); + db.getDatabase().setName("Test Database"); + assertEquals("Test Database", db.getDatabase().getName()); } @Test default void testGetSetDescription() { - assertNotNull(getDatabase().getDescription()); - getDatabase().setDescription("Test Description"); - assertEquals("Test Description", getDatabase().getDescription()); + DatabaseTestBase db = getDbFunc().get(); + + assertNotNull(db.getDatabase().getDescription()); + db.getDatabase().setDescription("Test Description"); + assertEquals("Test Description", db.getDatabase().getDescription()); } @Test default void testDeleteEntry() { - Entry entry = getDatabase().newEntry(); - getDatabase().getRootGroup().addEntry(entry); + DatabaseTestBase db = getDbFunc().get(); + + Entry entry = db.getDatabase().newEntry(); + db.getDatabase().getRootGroup().addEntry(entry); UUID uuid = entry.getUuid(); - assertTrue(getDatabase().deleteEntry(uuid)); - assertNull(getDatabase().findEntry(uuid)); + assertTrue(db.getDatabase().deleteEntry(uuid)); + assertNull(db.getDatabase().findEntry(uuid)); } @Test default void testDeleteGroup() { - Group group = getDatabase().newGroup(); - getDatabase().getRootGroup().addGroup(group); + DatabaseTestBase db = getDbFunc().get(); + + Group group = db.getDatabase().newGroup(); + db.getDatabase().getRootGroup().addGroup(group); UUID uuid = group.getUuid(); - assertTrue(getDatabase().deleteGroup(uuid)); - assertNull(getDatabase().findGroup(uuid)); + assertTrue(db.getDatabase().deleteGroup(uuid)); + assertNull(db.getDatabase().findGroup(uuid)); } @Test default void testSupportsNonStandardPropertyNames() { + DatabaseTestBase db = getDbFunc().get(); + // check standard names are there - Entry entry = getDatabase().newEntry(); + Entry entry = db.getDatabase().newEntry(); for (String name : Entry.STANDARD_PROPERTY_NAMES) { assertNotNull(entry.getProperty(name)); } // add a random property - if (getDatabase().supportsNonStandardPropertyNames()) { + if (db.getDatabase().supportsNonStandardPropertyNames()) { entry.setProperty("random", "you never know"); assertEquals("you never know", entry.getProperty("random")); } else { @@ -135,12 +151,14 @@ default void testSupportsNonStandardPropertyNames() { @Test default void testSupportsBinaryProperties() { - if (getDatabase().supportsBinaryProperties()) { - Entry entry = getDatabase().newEntry(); + DatabaseTestBase db = getDbFunc().get(); + + if (db.getDatabase().supportsBinaryProperties()) { + Entry entry = db.getDatabase().newEntry(); entry.setBinaryProperty("binary", new byte[]{1, 2, 3}); assertArrayEquals(new byte[]{1, 2, 3}, entry.getBinaryProperty("binary")); } else { - Entry entry = getDatabase().newEntry(); + Entry entry = db.getDatabase().newEntry(); assertThrows(UnsupportedOperationException.class, () -> entry.setBinaryProperty("binary", new byte[]{1, 2, 3})); } } diff --git a/database/src/test/resources/junit-platform.properties b/database/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..2115a2f1 --- /dev/null +++ b/database/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled = true diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java index 2fdaf772..4797eb31 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java @@ -17,6 +17,7 @@ package org.linguafranca.pwdb.kdb; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Visitor; import org.linguafranca.pwdb.test.DatabaseTestBase; @@ -26,24 +27,22 @@ import java.io.IOException; import java.io.InputStream; +import java.util.function.Supplier; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** * @author Jo */ -public class KdbDatabaseTest - extends - DatabaseTestBase - implements - GroupsAndEntriesTest, - Test123Test { +@Execution(CONCURRENT) +public class KdbDatabaseTest implements GroupsAndEntriesTest, Test123Test { - KdbDatabaseTest() { - super(KdbDatabase::new, KdbDatabase::loadNx, Database::saveNx, KdbCredentials.Password::new); - newDatabase(); + public static final Supplier kdbxFunc = ()-> new DatabaseTestBase(KdbDatabase::new, KdbDatabase::loadNx, Database::saveNx, KdbCredentials.Password::new); + + public Supplier getDbFunc() { + return kdbxFunc; } @Override @@ -58,25 +57,36 @@ public String getFileName(){ @Test public void supportedFunctionalityTest(){ - assertFalse(getDatabase().supportsBinaryProperties()); - assertFalse(getDatabase().supportsNonStandardPropertyNames()); - assertFalse(getDatabase().supportsRecycleBin()); - assertFalse(getDatabase().supportsPropertyValueStrategy()); + DatabaseTestBase testBase = getDbFunc().get(); + + assertFalse(testBase.getDatabase().supportsBinaryProperties()); + assertFalse(testBase.getDatabase().supportsNonStandardPropertyNames()); + assertFalse(testBase.getDatabase().supportsRecycleBin()); + assertFalse(testBase.getDatabase().supportsPropertyValueStrategy()); } @Test public void testCreateKdbDatabase() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); - Database database = KdbDatabase.load(new KdbCredentials.Password("123".getBytes()), inputStream); + Database database; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb")) { + assertNotNull(inputStream); + database = KdbDatabase.load(new KdbCredentials.Password("123".getBytes()), inputStream); + } database.visit(new Visitor.Print(getTestPrintStream())); } @Test public void openKdbWithKeyFile() throws IOException { - InputStream key = getClass().getClassLoader().getResourceAsStream("kdb.key"); - KdbCredentials creds = new KdbCredentials.KeyFile("123".getBytes(), key); - InputStream is = getClass().getClassLoader().getResourceAsStream("kdbwithkey.kdb"); - KdbDatabase db = KdbDatabase.load(creds, is); + KdbCredentials creds; + try (InputStream key = getClass().getClassLoader().getResourceAsStream("kdb.key")) { + assertNotNull(key); + creds = new KdbCredentials.KeyFile("123".getBytes(), key); + } + KdbDatabase db; + try (InputStream is = getClass().getClassLoader().getResourceAsStream("kdbwithkey.kdb")) { + assertNotNull(is); + db = KdbDatabase.load(creds, is); + } assertEquals(1, db.getRootGroup().getGroupsCount()); assertEquals("General", db.getRootGroup().getGroups().get(0).getName()); } diff --git a/kdb/src/test/resources/junit-platform.properties b/kdb/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..2115a2f1 --- /dev/null +++ b/kdb/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled = true diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java index ff0a7c90..17d234ce 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/Kdbx123Test.java @@ -17,6 +17,7 @@ package org.linguafranca.pwdb.kdbx.database; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.format.KdbxCredentials; @@ -26,12 +27,13 @@ import java.io.IOException; import java.io.InputStream; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; + /** * @author jo */ -public class Kdbx123Test - extends KdbxTestBase - implements Test123Test { +@Execution(CONCURRENT) +public class Kdbx123Test extends KdbxTestBase implements Test123Test { @Override public boolean getSkipDateCheck() { diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java index 62601b5d..e23ed11c 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java @@ -17,28 +17,27 @@ package org.linguafranca.pwdb.kdbx.database; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.test.BinaryPropertiesTest; +import org.linguafranca.pwdb.test.DatabaseTestBase; + +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; public class KdbxBinariesTest { - @Nested - public class KdbxBinariesV4Test - extends - KdbxTestBase - implements - BinaryPropertiesTest { + @Execution(CONCURRENT) + public static class KdbxBinariesV4Test extends KdbxTestBase implements BinaryPropertiesTest { KdbxBinariesV4Test() { - database = loadDatabase("123".getBytes(), "V4-ChaCha20-Argon2-Attachment.kdbx"); + DatabaseTestBase db = getDbFunc().get(); + db.loadAndInitDatabase("123".getBytes(), "V4-ChaCha20-Argon2-Attachment.kdbx"); } } - @Nested - public class KdbxBinariesV3Test - extends - KdbxTestBase - implements - BinaryPropertiesTest { + + @Execution(CONCURRENT) + public static class KdbxBinariesV3Test extends KdbxTestBase implements BinaryPropertiesTest { KdbxBinariesV3Test() { - database = loadDatabase("123".getBytes(), "Attachment.kdbx"); + DatabaseTestBase db = getDbFunc().get(); + db.loadAndInitDatabase("123".getBytes(), "Attachment.kdbx"); } } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java index 4cf6bfa2..d0326287 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java @@ -20,35 +20,48 @@ import java.io.PrintStream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.Visitor; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; +@Execution(CONCURRENT) public class KdbxDatabaseLoadTest { - static PrintStream printStream = getTestPrintStream(); - - @Test public void loadXml() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("ExampleDatabase.xml"); - KdbxDatabase database = KdbxDatabase.loadXml(inputStream); + PrintStream printStream = getTestPrintStream(); + KdbxDatabase database; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("ExampleDatabase.xml")) { + assertNotNull(inputStream); + database = KdbxDatabase.loadXml(inputStream); + } database.visit(new Visitor.Print(printStream)); } @Test public void loadKdbx() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx"); - KdbxDatabase database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + PrintStream printStream = getTestPrintStream(); + KdbxDatabase database; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx")) { + assertNotNull(inputStream); + database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + } database.visit(new Visitor.Print(printStream)); } @Test public void loadKdbxV4() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx"); - KdbxDatabase database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + PrintStream printStream = getTestPrintStream(); + KdbxDatabase database; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx")) { + assertNotNull(inputStream); + database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + } database.visit(new Visitor.Print(printStream)); // test what happens to dates in V4 database.visit(new Visitor.Default(){ @@ -61,15 +74,20 @@ public void visit(Entry entry) { @Test public void emptyDb() throws Exception { + PrintStream printStream = getTestPrintStream(); KdbxDatabase database = new KdbxDatabase(); printStream.println(database.getDescription()); } @Test public void dbWithDeleted() throws Exception { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("testDeleted.kdbx"); - KdbxDatabase database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); - database.visit(new Visitor.Print(printStream)); + PrintStream printStream = getTestPrintStream(); + KdbxDatabase database; + try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("testDeleted.kdbx")) { + assertNotNull(inputStream); + database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + } + database.visit(new Visitor.Print(printStream)); } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java index d2781a5f..4dbdf590 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseTest.java @@ -16,8 +16,12 @@ */ package org.linguafranca.pwdb.kdbx.database; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.test.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; + +@Execution(CONCURRENT) public class KdbxDatabaseTest extends KdbxTestBase @@ -26,4 +30,6 @@ public class KdbxDatabaseTest GroupsAndEntriesTest, RecycleBinTest, ProtectedPropertyTest, - ProtectedPropertyTest2 { } + ProtectedPropertyTest2 { + +} diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java index 9af60a1b..c5998d0f 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxSaveAndReloadTest.java @@ -16,13 +16,15 @@ */ package org.linguafranca.pwdb.kdbx.database; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.StreamFormat; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.test.*; -public class KdbxSaveAndReloadTest - extends KdbxTestBase - implements KdbxFileSaveAndReloadTest { +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; + +@Execution(CONCURRENT) +public class KdbxSaveAndReloadTest extends KdbxTestBase implements KdbxFileSaveAndReloadTest { @Override diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxTestBase.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxTestBase.java index ac350067..7c6d0d2f 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxTestBase.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxTestBase.java @@ -22,11 +22,14 @@ import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.test.*; -public abstract class KdbxTestBase - extends - DatabaseTestBase { +import java.util.function.Supplier; - KdbxTestBase() { - super(KdbxDatabase::new, KdbxDatabase::loadNx, Database::saveNx, KdbxCredentials::new); +public abstract class KdbxTestBase { + + public static final Supplier kdbxFunc = ()-> new DatabaseTestBase(KdbxDatabase::new, KdbxDatabase::loadNx, Database::saveNx, KdbxCredentials::new); + + public Supplier getDbFunc() { + return kdbxFunc; } + } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java index 565ea703..1019eef2 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java @@ -19,6 +19,7 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.format.KdbxCredentials; @@ -34,15 +35,19 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; +@Execution(CONCURRENT) public class Issue27Test { - static PrintStream printStream = getTestPrintStream(); + /** * Check load of problem file */ @Test public void testIssue27() throws IOException { + PrintStream printStream = getTestPrintStream(); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); KdbxDatabase db = KdbxDatabase.load(creds, is); @@ -59,6 +64,8 @@ public void testIssue27() throws IOException { */ @Test public void testV4Date() throws IOException { + PrintStream printStream = getTestPrintStream(); + InputStream is = this.getClass().getClassLoader().getResourceAsStream("V4-AES-AES.kdbx"); KdbxCredentials creds = new KdbxCredentials("123".getBytes()); KdbxDatabase db = KdbxDatabase.load(creds, is); @@ -72,6 +79,8 @@ public void testV4Date() throws IOException { @Test public void testIssue27XML() throws IOException { + PrintStream printStream = getTestPrintStream(); + InputStream is = Issue27Test.class.getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java index 14b9981b..113b9c70 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,50 +18,51 @@ package org.linguafranca.pwdb.kdbx.database.validation; - - -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Credentials; import org.linguafranca.pwdb.StreamFormat; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.format.KdbxCredentials; -import org.linguafranca.pwdb.kdbx.jackson.util.Util; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; +import org.linguafranca.pwdb.kdbx.jackson.util.Util; -import java.io.*; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; import java.nio.file.Files; -import java.nio.file.Paths; +import java.nio.file.Path; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; /** * Review Issue-33 */ +@Execution(CONCURRENT) public class Issue33Test { public static final String TEST_RESOURCE = "V4-AES-Argon2-CustomIcon.kdbx"; - public static final String TEST_OUTPUT_DIR = "testOutput"; - public static final File TEST_XML_FILE = Paths.get(TEST_OUTPUT_DIR,"Issue33Source.xml").toFile(); public static final KdbxCredentials CREDENTIALS = new KdbxCredentials("123".getBytes()); - InputStream inputStream; + public static void listXml(Path tempPath) throws IOException { + final Path TEST_XML_FILE = tempPath.resolve("Issue33Source.xml"); - @BeforeAll - public static void listXml() throws IOException { - Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); - Util.listXml(TEST_RESOURCE, CREDENTIALS, new PrintWriter(TEST_XML_FILE)); + Util.listXml(TEST_RESOURCE, CREDENTIALS, new PrintWriter(System.out)); Helpers.isV4.set(true); } - @BeforeEach - public void refreshInputStream() { - inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE); - } - @Test - public void testJacksonDatabase() throws IOException { - KdbxDatabase database = KdbxDatabase.load(CREDENTIALS, inputStream); - database.save(new StreamFormat.None(), new Credentials.None(), Files.newOutputStream(Paths.get(TEST_OUTPUT_DIR, "Issue33Jackson.xml"))); + public void testJacksonDatabase(@TempDir Path tempPath) throws IOException { + listXml(tempPath); + + KdbxDatabase database; + try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE)) { + assertNotNull(inputStream); + database = KdbxDatabase.load(CREDENTIALS, inputStream); + } + database.save(new StreamFormat.None(), new Credentials.None(), Files.newOutputStream(tempPath.resolve("Issue33Jackson.xml"))); } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java index b49f3a04..e383da16 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java @@ -18,6 +18,7 @@ package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; @@ -27,16 +28,19 @@ import java.io.PrintStream; import java.util.List; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** * Bug report on GitHub, the Keyfile is Version 2 (Hex) */ +@Execution(CONCURRENT) public class Issue38Test { - static PrintStream printStream = getTestPrintStream(); @Test public void testV2Keyfile() throws IOException { + PrintStream printStream = getTestPrintStream(); + InputStream databaseStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database.kdbx"); InputStream keyStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database.keyx"); assert keyStream != null; diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java index d46f3300..f24c15e2 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java @@ -18,15 +18,20 @@ package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import java.io.IOException; import java.io.InputStream; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; + /** * Review Issue-70 */ +@Execution(CONCURRENT) public class Issue70Test { public static final String TEST_RESOURCE1 = "issue-70/test2.kdbx"; @@ -41,6 +46,7 @@ public class Issue70Test { @Test public void testCustomData() throws IOException { try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + assertNotNull(inputStream); KdbxDatabase database = KdbxDatabase.load(CREDENTIALS1, inputStream); } } @@ -48,6 +54,7 @@ public void testCustomData() throws IOException { @Test public void testFileFormat_4_1() throws IOException { try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE2)){ + assertNotNull(inputStream); KdbxDatabase database = KdbxDatabase.load(CREDENTIALS2, inputStream); } } @@ -56,6 +63,7 @@ public void testFileFormat_4_1() throws IOException { @Test public void testCustomData2() throws IOException { try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE3)){ + assertNotNull(inputStream); KdbxDatabase database = KdbxDatabase.load(CREDENTIALS3, inputStream); } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java index 844f21dc..5d1b088e 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java @@ -1,6 +1,7 @@ package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; @@ -19,23 +20,24 @@ import java.util.UUID; import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.pwdb.kdbx.jackson.util.Util.streamToString; /** * Review Issue-87 ... */ +@Execution(CONCURRENT) public class Issue87Test { - public static final String TEST_RESOURCE1 = "issue-87/customIcon-123.kdbx"; public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials("123".getBytes()); public static final String TEST_OUTPUT_ISSUE_87_KDBX = "testOutput/Issue87.kdbx"; - @Test public void testCustomIcon() throws IOException { KdbxDatabase database; try (InputStream inputStream = Issue87Test.class.getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + assertNotNull(inputStream); database = KdbxDatabase.load(CREDENTIALS1, inputStream); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java index f5324c39..6000fe0d 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java @@ -1,6 +1,7 @@ package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -13,11 +14,14 @@ import java.nio.file.Path; import java.nio.file.Paths; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.pwdb.kdbx.jackson.util.Util.streamToString; /** * Review Issue-88 ... */ +@Execution(CONCURRENT) public class Issue88Test { @@ -30,6 +34,7 @@ public class Issue88Test { public void testDefaultAutoType() throws IOException { KdbxDatabase database; try (InputStream inputStream = Issue88Test.class.getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + assertNotNull(inputStream); database = KdbxDatabase.load(CREDENTIALS1, inputStream); } diff --git a/kdbx-database/src/test/resources/junit-platform.properties b/kdbx-database/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..2115a2f1 --- /dev/null +++ b/kdbx-database/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled = true diff --git a/kdbx-io/src/test/resources/junit-platform.properties b/kdbx-io/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..2115a2f1 --- /dev/null +++ b/kdbx-io/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled = true diff --git a/pom.xml b/pom.xml index 60991e98..e03b851d 100644 --- a/pom.xml +++ b/pom.xml @@ -35,8 +35,8 @@ pom KeePassJava2 - A Java 11 API for databases compatible with the renowned KeePass password safe for Windows - https://github.com/jorabin/KeePassJava2 + A Java 25 API for databases compatible with the renowned KeePass password safe for Windows + https://github.com/sergmain/KeePassJava2 @@ -47,9 +47,9 @@ - scm:git:git://github.com/jorabin/KeePassJava2.git - scm:git:git@github.com:jorabin/KeePassJava2.git - https://github.com/jorabin/KeePassJava2 + scm:git:git://github.com/sergmain/KeePassJava2.git + scm:git:git@github.com:sergmain/KeePassJava2.git + https://github.com/sergmain/KeePassJava2 KeePassJava2-3.0.0 @@ -62,13 +62,19 @@ Creator + + Sergio Lissner + + AI Supervising Engineer + + UTF-8 - 11 + 25 - 3.13.0 + 3.15.0 3.5.2 1.7.0 3.3.1 @@ -125,16 +131,27 @@ - - org.apache.maven.plugins - maven-compiler-plugin - ${maven-compiler-plugin.version} - - ${java.version} - ${java.version} - - - + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + --enable-preview + -parameters + ${java.version} + ${project.build.sourceEncoding} + true + + + + org.apache.maven.plugins maven-release-plugin ${maven-release-plugin.version} @@ -240,7 +257,7 @@ org.bouncycastle bcpkix-jdk18on - 1.83 + 1.84 org.slf4j @@ -289,7 +306,7 @@ releaseToMaven - + org.apache.maven.plugins maven-gpg-plugin ${maven-gpg-plugin.version} From 687cab8de79abf37136c9ab106a2797f6fa37e29 Mon Sep 17 00:00:00 2001 From: sergmain Date: Fri, 8 May 2026 12:42:53 -0700 Subject: [PATCH 63/66] novacrypto was removed because of violation APL 2.0 licence type --- example/pom.xml | 5 - .../SecureCharBufferPropertyValue.java | 101 ------------------ .../linguafranca/pwdb/kdbx/QuickStart.java | 23 ++-- .../test/resources/junit-platform.properties | 1 + 4 files changed, 13 insertions(+), 117 deletions(-) delete mode 100644 example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java create mode 100644 example/src/test/resources/junit-platform.properties diff --git a/example/pom.xml b/example/pom.xml index 1da64fd3..94edd0c8 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -39,11 +39,6 @@ KeePassJava2 ${project.version} - - io.github.novacrypto - SecureString - 2022.01.17 - org.slf4j slf4j-api diff --git a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java deleted file mode 100644 index 8b2708f0..00000000 --- a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.example; - -import io.github.novacrypto.SecureCharBuffer; -import org.linguafranca.pwdb.PropertyValue; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.StandardCharsets; - -/** - * This is an example of a PropertyValue implementation that uses {@link SecureCharBuffer} - * to store the value in memory. - */ -public class SecureCharBufferPropertyValue implements PropertyValue { - private final SecureCharBuffer value; - - /** - * Builder for {@link SecureCharBufferPropertyValue} - */ - static class Factory implements PropertyValue.Factory { - - @Override - public SecureCharBufferPropertyValue of(CharSequence aString) { - return new SecureCharBufferPropertyValue(aString); - } - - @Override - public SecureCharBufferPropertyValue of(char[] value) { - return new SecureCharBufferPropertyValue(value); - } - - @Override - public SecureCharBufferPropertyValue of(byte[] value) { - return new SecureCharBufferPropertyValue(value); - } - } - - public SecureCharBufferPropertyValue(CharSequence aString) { - this.value = SecureCharBuffer.withCapacity(aString.length()); - this.value.append(aString); - } - - public SecureCharBufferPropertyValue(char [] value) { - this.value = SecureCharBuffer.withCapacity(value.length); - this.value.append(CharBuffer.wrap(value)); - } - - public SecureCharBufferPropertyValue(byte [] value) { - this.value = SecureCharBuffer.withCapacity(value.length); - this.value.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(value))); - } - @Override - public String getValueAsString() { - return this.value.toStringAble().toString(); - } - - @Override - public CharSequence getValue() { - return this.value; - } - - @Override - public char [] getValueAsChars() { - CharBuffer cb = CharBuffer.wrap(value); - char[] result = new char[cb.limit()]; - cb.get(result); - return result; - } - - @Override - public byte [] getValueAsBytes() { - CharBuffer cb = CharBuffer.wrap(value); - ByteBuffer bb = StandardCharsets.UTF_8.encode(cb); - byte[] result = new byte[bb.limit()]; - bb.get(result); - return result; - } - - @Override - public boolean isProtected() { - return true; - } -} - diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 643fac02..2036e252 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -31,6 +31,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; +import java.util.Objects; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -46,7 +47,7 @@ public abstract class QuickStart { public static final String TEST_OUTPUT_DIR = "testOutput/"; static PrintStream printStream = getTestPrintStream(); - public void canonicalQuickStart() throws IOException { + public static void canonicalQuickStart() throws IOException { // V3 create a database, no need to specify what kind (e.g. JacksonDatabase) // V3 Database no longer need to be generified @@ -82,7 +83,7 @@ public void canonicalQuickStart() throws IOException { /** * Illustrates the fluent API for creating a database, as well as the various ways of setting property values. */ - public void fluentExample() { + public static void fluentExample() { Database database = new KdbxDatabase(); database.getRootGroup().addGroup("Group 1") .addEntry("as property values determined by the database property value strategy (preferred)") @@ -113,7 +114,7 @@ public void loadKdbx() throws IOException { // password credentials Credentials credentials = new KdbxCredentials("123".getBytes()); // load the database from the input stream - Database database = KdbxDatabase.load(credentials, inputStream); + Database database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); // visit all groups and entries and list them to console database.visit(new Visitor.Print(printStream)); } @@ -123,11 +124,11 @@ public void loadKdbx() throws IOException { * Save KDBX to file */ - private Entry entryFactory(Database database, String s, int e) { + private static Entry entryFactory(Database database, String s, int e) { return database.newEntry(String.format("Group %s Entry %d", s, e)); } - public void saveKdbx() throws IOException { + public static void saveKdbx() throws IOException { // create an empty database Database database = new KdbxDatabase(); @@ -149,7 +150,7 @@ public void saveKdbx() throws IOException { /** * Splice - add a group from one database to a parent from another (or copy from the same db) */ - public void splice(Group newParent, Group groupToSplice) { + public static void splice(Group newParent, Group groupToSplice) { Group addedGroup = newParent.addGroup(newParent.getDatabase().newGroup(groupToSplice)); addedGroup.copy(groupToSplice); } @@ -157,7 +158,7 @@ public void splice(Group newParent, Group groupToSplice) { /** * Group by title */ - public Group groupByTitle(Database database) { + public static Group groupByTitle(Database database) { List entries = database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); // create a new group using DB factory method @@ -180,7 +181,7 @@ public void loadKdbSaveKdbx() throws IOException { // password credentials Credentials credentials = new KdbCredentials.Password("123".getBytes()); // load KdbDatabase - KdbDatabase database = KdbDatabase.load(credentials, inputStream); + KdbDatabase database = KdbDatabase.load(credentials, Objects.requireNonNull(inputStream)); // create a KDBX (database Database kdbxDatabase = new KdbxDatabase(); @@ -203,7 +204,7 @@ public void loadKdbx3SaveKdbx4(String v3ResourceName, byte[] password, Path v4Fi KdbxCredentials credentials = new KdbxCredentials(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(v3ResourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); } // create a KDBX (database @@ -238,7 +239,7 @@ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3File KdbxCredentials credentials = new KdbxCredentials(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); } // create a KDBX database @@ -267,7 +268,7 @@ public void loadKdbx4SaveToConsole(String resourceName, byte[] password, Path pa KdbxCredentials credentials = new KdbxCredentials(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); } database.save(new StreamFormat.None(), new Credentials.None(), TestUtil.getTestPrintStream()); diff --git a/example/src/test/resources/junit-platform.properties b/example/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..2115a2f1 --- /dev/null +++ b/example/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled = true From 98824e9574152505ff5d3c7bf5303abdaf67ac0e Mon Sep 17 00:00:00 2001 From: sergmain Date: Fri, 8 May 2026 12:43:15 -0700 Subject: [PATCH 64/66] Revert "novacrypto was removed because of violation APL 2.0 licence type" This reverts commit 687cab8de79abf37136c9ab106a2797f6fa37e29. --- example/pom.xml | 5 + .../SecureCharBufferPropertyValue.java | 101 ++++++++++++++++++ .../linguafranca/pwdb/kdbx/QuickStart.java | 23 ++-- .../test/resources/junit-platform.properties | 1 - 4 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java delete mode 100644 example/src/test/resources/junit-platform.properties diff --git a/example/pom.xml b/example/pom.xml index 94edd0c8..1da64fd3 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -39,6 +39,11 @@ KeePassJava2 ${project.version} + + io.github.novacrypto + SecureString + 2022.01.17 + org.slf4j slf4j-api diff --git a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java new file mode 100644 index 00000000..8b2708f0 --- /dev/null +++ b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2025. Jo Rabin + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.linguafranca.pwdb.example; + +import io.github.novacrypto.SecureCharBuffer; +import org.linguafranca.pwdb.PropertyValue; + +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.StandardCharsets; + +/** + * This is an example of a PropertyValue implementation that uses {@link SecureCharBuffer} + * to store the value in memory. + */ +public class SecureCharBufferPropertyValue implements PropertyValue { + private final SecureCharBuffer value; + + /** + * Builder for {@link SecureCharBufferPropertyValue} + */ + static class Factory implements PropertyValue.Factory { + + @Override + public SecureCharBufferPropertyValue of(CharSequence aString) { + return new SecureCharBufferPropertyValue(aString); + } + + @Override + public SecureCharBufferPropertyValue of(char[] value) { + return new SecureCharBufferPropertyValue(value); + } + + @Override + public SecureCharBufferPropertyValue of(byte[] value) { + return new SecureCharBufferPropertyValue(value); + } + } + + public SecureCharBufferPropertyValue(CharSequence aString) { + this.value = SecureCharBuffer.withCapacity(aString.length()); + this.value.append(aString); + } + + public SecureCharBufferPropertyValue(char [] value) { + this.value = SecureCharBuffer.withCapacity(value.length); + this.value.append(CharBuffer.wrap(value)); + } + + public SecureCharBufferPropertyValue(byte [] value) { + this.value = SecureCharBuffer.withCapacity(value.length); + this.value.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(value))); + } + @Override + public String getValueAsString() { + return this.value.toStringAble().toString(); + } + + @Override + public CharSequence getValue() { + return this.value; + } + + @Override + public char [] getValueAsChars() { + CharBuffer cb = CharBuffer.wrap(value); + char[] result = new char[cb.limit()]; + cb.get(result); + return result; + } + + @Override + public byte [] getValueAsBytes() { + CharBuffer cb = CharBuffer.wrap(value); + ByteBuffer bb = StandardCharsets.UTF_8.encode(cb); + byte[] result = new byte[bb.limit()]; + bb.get(result); + return result; + } + + @Override + public boolean isProtected() { + return true; + } +} + diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 2036e252..643fac02 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -31,7 +31,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.List; -import java.util.Objects; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -47,7 +46,7 @@ public abstract class QuickStart { public static final String TEST_OUTPUT_DIR = "testOutput/"; static PrintStream printStream = getTestPrintStream(); - public static void canonicalQuickStart() throws IOException { + public void canonicalQuickStart() throws IOException { // V3 create a database, no need to specify what kind (e.g. JacksonDatabase) // V3 Database no longer need to be generified @@ -83,7 +82,7 @@ public static void canonicalQuickStart() throws IOException { /** * Illustrates the fluent API for creating a database, as well as the various ways of setting property values. */ - public static void fluentExample() { + public void fluentExample() { Database database = new KdbxDatabase(); database.getRootGroup().addGroup("Group 1") .addEntry("as property values determined by the database property value strategy (preferred)") @@ -114,7 +113,7 @@ public void loadKdbx() throws IOException { // password credentials Credentials credentials = new KdbxCredentials("123".getBytes()); // load the database from the input stream - Database database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); + Database database = KdbxDatabase.load(credentials, inputStream); // visit all groups and entries and list them to console database.visit(new Visitor.Print(printStream)); } @@ -124,11 +123,11 @@ public void loadKdbx() throws IOException { * Save KDBX to file */ - private static Entry entryFactory(Database database, String s, int e) { + private Entry entryFactory(Database database, String s, int e) { return database.newEntry(String.format("Group %s Entry %d", s, e)); } - public static void saveKdbx() throws IOException { + public void saveKdbx() throws IOException { // create an empty database Database database = new KdbxDatabase(); @@ -150,7 +149,7 @@ public static void saveKdbx() throws IOException { /** * Splice - add a group from one database to a parent from another (or copy from the same db) */ - public static void splice(Group newParent, Group groupToSplice) { + public void splice(Group newParent, Group groupToSplice) { Group addedGroup = newParent.addGroup(newParent.getDatabase().newGroup(groupToSplice)); addedGroup.copy(groupToSplice); } @@ -158,7 +157,7 @@ public static void splice(Group newParent, Group groupToSplice) { /** * Group by title */ - public static Group groupByTitle(Database database) { + public Group groupByTitle(Database database) { List entries = database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); // create a new group using DB factory method @@ -181,7 +180,7 @@ public void loadKdbSaveKdbx() throws IOException { // password credentials Credentials credentials = new KdbCredentials.Password("123".getBytes()); // load KdbDatabase - KdbDatabase database = KdbDatabase.load(credentials, Objects.requireNonNull(inputStream)); + KdbDatabase database = KdbDatabase.load(credentials, inputStream); // create a KDBX (database Database kdbxDatabase = new KdbxDatabase(); @@ -204,7 +203,7 @@ public void loadKdbx3SaveKdbx4(String v3ResourceName, byte[] password, Path v4Fi KdbxCredentials credentials = new KdbxCredentials(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(v3ResourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); + database = KdbxDatabase.load(credentials, inputStream); } // create a KDBX (database @@ -239,7 +238,7 @@ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3File KdbxCredentials credentials = new KdbxCredentials(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); + database = KdbxDatabase.load(credentials, inputStream); } // create a KDBX database @@ -268,7 +267,7 @@ public void loadKdbx4SaveToConsole(String resourceName, byte[] password, Path pa KdbxCredentials credentials = new KdbxCredentials(password); try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); + database = KdbxDatabase.load(credentials, inputStream); } database.save(new StreamFormat.None(), new Credentials.None(), TestUtil.getTestPrintStream()); diff --git a/example/src/test/resources/junit-platform.properties b/example/src/test/resources/junit-platform.properties deleted file mode 100644 index 2115a2f1..00000000 --- a/example/src/test/resources/junit-platform.properties +++ /dev/null @@ -1 +0,0 @@ -junit.jupiter.execution.parallel.enabled = true From c241d68a8c7be65511428290e11b71956fe9d194 Mon Sep 17 00:00:00 2001 From: sergmain Date: Fri, 8 May 2026 15:26:56 -0700 Subject: [PATCH 65/66] removing com.fasterxml.woodstox --- all/pom.xml | 6 +- basic/pom.xml | 12 +-- .../pwdb/basic/BasicDatabaseKdbxTest.java | 5 +- .../linguafranca/pwdb/basic/package-info.java | 19 +--- database/pom.xml | 2 +- .../java/org/linguafranca/pwdb/KdbConsts.java | 10 ++ .../pwdb/test/BinaryPropertiesTest.java | 9 +- .../pwdb/test/KdbxFileSaveAndReloadTest.java | 66 +++++++----- .../pwdb/test/ProtectedPropertyTest2.java | 22 ++-- .../linguafranca/pwdb/test/Test123Test.java | 7 +- .../java/org/linguafranca/util/TestUtil.java | 12 +-- example/pom.xml | 11 +- .../SecureCharBufferPropertyValue.java | 101 ------------------ .../linguafranca/pwdb/kdbx/OpenDbExample.java | 3 +- .../linguafranca/pwdb/kdbx/QuickStart.java | 82 +++++++------- .../linguafranca/pwdb/kdbx/SaxParseTest.java | 11 +- .../pwdb/kdbx/SimpleQuickStartTest.java | 66 +++++++----- kdb/pom.xml | 6 +- .../pwdb/kdb/KdbDatabaseTest.java | 5 +- kdbx-database/pom.xml | 13 +-- .../jackson/KdbxSerializableDatabase.java | 12 +-- .../converter/StringToDateConverter.java | 3 +- .../pwdb/kdbx/jackson/util/Util.java | 22 ++-- .../pwdb/kdbx/database/KdbxBinariesTest.java | 5 +- .../kdbx/database/KdbxDatabaseLoadTest.java | 15 +-- .../pwdb/kdbx/database/package-info.java | 21 ++++ .../pwdb/kdbx/database/util/UtilTest.java | 31 +++--- .../pwdb/kdbx/database/util/package-info.java | 21 ++++ .../kdbx/database/validation/Issue27Test.java | 33 ++++-- .../kdbx/database/validation/Issue33Test.java | 18 +++- .../kdbx/database/validation/Issue38Test.java | 17 +-- .../kdbx/database/validation/Issue70Test.java | 15 +-- .../kdbx/database/validation/Issue87Test.java | 14 +-- .../kdbx/database/validation/Issue88Test.java | 16 +-- .../database/validation/package-info.java | 21 ++++ kdbx-io/pom.xml | 6 +- .../pwdb/kdbx/KdbxHeaderTest.java | 3 +- .../pwdb/kdbx/KdbxKeyFileTest.java | 11 +- .../pwdb/kdbx/KdbxSerializerTest.java | 9 +- pom.xml | 2 +- 40 files changed, 397 insertions(+), 366 deletions(-) rename example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java => basic/src/test/java/org/linguafranca/pwdb/basic/package-info.java (65%) create mode 100644 database/src/main/java/org/linguafranca/pwdb/KdbConsts.java delete mode 100644 example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/package-info.java create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/package-info.java create mode 100644 kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/package-info.java diff --git a/all/pom.xml b/all/pom.xml index ba081180..42ecd0ea 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -19,7 +19,7 @@ KeePassJava2.parent - org.linguafranca.pwdb + mh.keepass 3.0.0-SNAPSHOT ../pom.xml @@ -32,12 +32,12 @@ - org.linguafranca.pwdb + mh.keepass KeePassJava2.kdb ${project.version} - org.linguafranca.pwdb + mh.keepass KeePassJava2.kdbx.database ${project.version} diff --git a/basic/pom.xml b/basic/pom.xml index 2efda25b..1d0868c8 100644 --- a/basic/pom.xml +++ b/basic/pom.xml @@ -21,7 +21,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.linguafranca.pwdb + mh.keepass KeePassJava2.parent 3.0.0-SNAPSHOT @@ -35,13 +35,13 @@ - org.linguafranca.pwdb + mh.keepass database ${project.version} compile - org.linguafranca.pwdb + mh.keepass KeePassJava2.kdbx.io 3.0.0-SNAPSHOT compile @@ -49,12 +49,12 @@ com.fasterxml.jackson.dataformat jackson-dataformat-xml - 2.18.2 + 2.21.2 com.fasterxml.jackson.datatype jackson-datatype-jsr310 - 2.18.2 + 2.21.2 com.fasterxml.woodstox @@ -62,7 +62,7 @@ 7.1.0 - org.linguafranca.pwdb + mh.keepass database ${project.version} test-jar diff --git a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java index 64e9bbbc..47409b70 100644 --- a/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/BasicDatabaseKdbxTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -57,10 +58,10 @@ public void testKdbx(@TempDir Path tempPath) throws IOException { .addProperty(USER_NAME,"user"); Path dbPath = tempPath.resolve("test.kdbx"); - db.save(new KdbxStreamFormat(), new KdbxCredentials("123".getBytes()), Files.newOutputStream(dbPath)); + db.save(new KdbxStreamFormat(), new KdbxCredentials(KdbConsts.PASSWORD_BYTES), Files.newOutputStream(dbPath)); KdbxHeader header = new KdbxHeader(); - InputStream unencryptedInputStream = KdbxSerializer.createUnencryptedInputStream(new KdbxCredentials("123".getBytes()), + InputStream unencryptedInputStream = KdbxSerializer.createUnencryptedInputStream(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), header, Files.newInputStream(dbPath)); BasicDatabaseSerializer.Xml xml = new BasicDatabaseSerializer.Xml(header.getInnerStreamEncryptor()); diff --git a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java b/basic/src/test/java/org/linguafranca/pwdb/basic/package-info.java similarity index 65% rename from example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java rename to basic/src/test/java/org/linguafranca/pwdb/basic/package-info.java index d78f7f1c..5ecf339c 100644 --- a/example/src/test/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValueTest.java +++ b/basic/src/test/java/org/linguafranca/pwdb/basic/package-info.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,19 +15,7 @@ * limitations under the License. * */ +@NullMarked +package org.linguafranca.pwdb.basic; -package org.linguafranca.pwdb.example; - - -import org.junit.jupiter.api.Test; - -import static org.linguafranca.pwdb.PropertyValueUtil.genericTest; - -public class SecureCharBufferPropertyValueTest { - - @Test - public void storageTest(){ - genericTest(new SecureCharBufferPropertyValue.Factory(), false); - } - -} \ No newline at end of file +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/database/pom.xml b/database/pom.xml index 6f9401e5..5b5e46ad 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -20,7 +20,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> KeePassJava2.parent - org.linguafranca.pwdb + mh.keepass 3.0.0-SNAPSHOT ../pom.xml diff --git a/database/src/main/java/org/linguafranca/pwdb/KdbConsts.java b/database/src/main/java/org/linguafranca/pwdb/KdbConsts.java new file mode 100644 index 00000000..78b44f4e --- /dev/null +++ b/database/src/main/java/org/linguafranca/pwdb/KdbConsts.java @@ -0,0 +1,10 @@ +package org.linguafranca.pwdb; + +/** + * @author Sergio Lissner + * Date: 5/8/2026 + * Time: 2:19 PM + */ +public class KdbConsts { + public static final byte[] PASSWORD_BYTES = "123".getBytes(); +} diff --git a/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java index 64972ba6..049ec6f0 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/BinaryPropertiesTest.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.KdbConsts; import java.io.IOException; import java.io.InputStream; @@ -142,9 +143,9 @@ default void saveAndReloadCheck() throws IOException { DatabaseTestBase testBase = getDbFunc().get(); Path file = Files.createTempFile("test8", "tmp"); - testBase.saveDatabase(testBase.getDatabase(), testBase.getCredentials("123".getBytes()), Files.newOutputStream(file)); + testBase.saveDatabase(testBase.getDatabase(), testBase.getCredentials(KdbConsts.PASSWORD_BYTES), Files.newOutputStream(file)); - Database db = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), Files.newInputStream(file)); + Database db = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), Files.newInputStream(file)); Entry newEntry = db.findEntries("Test attachment").get(0); assertArrayEquals(new String[]{"letter J.jpeg"}, newEntry.getBinaryPropertyNames().toArray()); Entry oldEntry = testBase.getDatabase().findEntries("Test attachment").get(0); @@ -173,9 +174,9 @@ default void createAndSaveCheck() throws IOException { assert testFile != null; byte[] letterJ = IOUtils.toByteArray(testFile); entry.setBinaryProperty("letter J.jpeg", letterJ); - testBase.saveDatabase(database1, testBase.getCredentials("123".getBytes()), Files.newOutputStream(file)); + testBase.saveDatabase(database1, testBase.getCredentials(KdbConsts.PASSWORD_BYTES), Files.newOutputStream(file)); - Database db = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), Files.newInputStream(file)); + Database db = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), Files.newInputStream(file)); Entry entry1 = db.findEntries("Test attachment").get(0); // just one property assertArrayEquals(new String[]{"letter J.jpeg"}, entry1.getBinaryPropertyNames().toArray()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java index f680977a..472f4b88 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/KdbxFileSaveAndReloadTest.java @@ -20,10 +20,12 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.linguafranca.pwdb.*; import java.io.*; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.UUID; import java.util.function.Supplier; @@ -48,7 +50,7 @@ public interface KdbxFileSaveAndReloadTest { * and also verifies that saving doesn't alter the contents */ @Test - default void saveAndReloadTest() throws IOException { + default void saveAndReloadTest(@TempDir Path tempPath) throws IOException { DatabaseTestBase testBase = getDbFunc().get(); long now = System.currentTimeMillis(); @@ -58,18 +60,21 @@ default void saveAndReloadTest() throws IOException { verifyContents(output); //output.save(new StreamFormat.None(), new Credentials.None(), printStream); - FileOutputStream fos = new FileOutputStream("testOutput/test1.kdbx"); - testBase.saveDatabase(output, testBase.getCredentials("123".getBytes()), fos); - assertFalse(output.isDirty()); - fos.flush(); - fos.close(); + Path dbPath = tempPath.resolve("test1.kdbx"); + try (OutputStream fos = Files.newOutputStream(dbPath)) { + testBase.saveDatabase(output, testBase.getCredentials(KdbConsts.PASSWORD_BYTES), fos); + assertFalse(output.isDirty()); + fos.flush(); + fos.close(); + } // make sure that saving didn't mess up content verifyContents(output); //output.save(new StreamFormat.None(), new Credentials.None(), printStream); - - FileInputStream fis = new FileInputStream("testOutput/test1.kdbx"); - Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); + Database input; + try (InputStream fis = Files.newInputStream(dbPath)) { + input = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), fis); + } verifyContents(input); } @@ -77,13 +82,13 @@ default void saveAndReloadTest() throws IOException { * Test verifies that attachments are saved and reloaded correctly */ @Test - default void saveAndReloadTest2() throws IOException { + default void saveAndReloadTest2(@TempDir Path tempDir) throws IOException { DatabaseTestBase testBase = getDbFunc().get(); Database attachment; try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx")) { assertNotNull(inputStream); - attachment = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), + attachment = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), inputStream); } @@ -98,13 +103,17 @@ default void saveAndReloadTest2() throws IOException { entry.setBinaryProperty("letter L.jpeg", content); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); - FileOutputStream fos = new FileOutputStream("testOutput/test2.kdbx"); - testBase.saveDatabase(attachment, testBase.getCredentials("123".getBytes()), fos); - fos.flush(); - fos.close(); + Path dbPath = tempDir.resolve("test2.kdbx"); + try (OutputStream fos = Files.newOutputStream(dbPath)) { + testBase.saveDatabase(attachment, testBase.getCredentials(KdbConsts.PASSWORD_BYTES), fos); + fos.flush(); + fos.close(); + } - FileInputStream fis = new FileInputStream("testOutput/test2.kdbx"); - Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); + Database input; + try (InputStream fis = Files.newInputStream(dbPath)) { + input = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), fis); + } entry = input.findEntries("Test attachment").get(0); assertArrayEquals(new String[] {"letter J.jpeg", "letter L.jpeg"}, entry.getBinaryPropertyNames().toArray()); @@ -121,21 +130,26 @@ default void saveAndReloadTest2() throws IOException { * Test verifies that database is saved with same encryption that it was loaded with */ @Test - default void saveAndReloadTest3() throws IOException { + default void saveAndReloadTest3(@TempDir Path tempPath) throws IOException { DatabaseTestBase testBase = getDbFunc().get(); for (String resource: testFiles) { Database database; try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resource)) { assertNotNull(inputStream); - database = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), inputStream); + database = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), inputStream); } StreamFormat format1 = database.getStreamFormat(); - database.save(testBase.getCredentials("123".getBytes()), Files.newOutputStream(Paths.get("testOutput/test3.kdbx"))); + Path dbPath = tempPath.resolve("test3.kdbx"); + try (OutputStream outputStream = Files.newOutputStream(dbPath)) { + database.save(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), outputStream); + } - FileInputStream fis = new FileInputStream("testOutput/test3.kdbx"); - Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); + Database input; + try (InputStream fis = Files.newInputStream(dbPath)) { + input = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), fis); + } StreamFormat format2 = input.getStreamFormat(); assertTrue(verifyStreamFormat(format1, format2)); } @@ -191,13 +205,15 @@ private static void verifyContents(Database database) { * @throws IOException when naughty */ @Disabled - default void saveNewDatabase () throws IOException { + default void saveNewDatabase(@TempDir Path tempPath) throws IOException { DatabaseTestBase testBase = getDbFunc().get(); Database database = createNewDatabase(); - FileOutputStream outputStream = new FileOutputStream("compatibility.kdbx"); - testBase.saveDatabase(database, testBase.getCredentials("123".getBytes()), outputStream); + Path dbPath = tempPath.resolve("compatibility.kdbx"); + try (OutputStream outputStream = Files.newOutputStream(dbPath)) { + testBase.saveDatabase(database, testBase.getCredentials(KdbConsts.PASSWORD_BYTES), outputStream); + } } /** diff --git a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java index 26ed2d17..d0799918 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/ProtectedPropertyTest2.java @@ -18,11 +18,15 @@ package org.linguafranca.pwdb.test; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.linguafranca.pwdb.Database; import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.PropertyValue; import java.io.*; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.List; import java.util.function.Consumer; import java.util.function.Supplier; @@ -105,7 +109,7 @@ default void checkDefaults() { } @Test - default void expectedStorageType() throws IOException { + default void expectedStorageType(@TempDir Path tempPath) throws IOException { DatabaseTestBase testBase = getDbFunc().get(); if (!testBase.getDatabase().supportsPropertyValueStrategy()) { @@ -145,14 +149,18 @@ default void expectedStorageType() throws IOException { // Save database - FileOutputStream fos = new FileOutputStream("testOutput/test9.kdbx"); - testBase.saveDatabase(testBase.getDatabase(), testBase.getCredentials("123".getBytes()), fos); - fos.flush(); - fos.close(); + Path dbPath = tempPath.resolve("test9.kdbx"); + try (OutputStream fos = Files.newOutputStream(dbPath)) { + testBase.saveDatabase(testBase.getDatabase(), testBase.getCredentials(KdbConsts.PASSWORD_BYTES), fos); + fos.flush(); + fos.close(); + } // reload database, "random" is still protected even though it's not protected by default - FileInputStream fis = new FileInputStream("testOutput/test9.kdbx"); - Database input = testBase.loadDatabase(testBase.getCredentials("123".getBytes()), fis); + Database input; + try (InputStream fis = Files.newInputStream(dbPath)) { + input = testBase.loadDatabase(testBase.getCredentials(KdbConsts.PASSWORD_BYTES), fis); + } List entries = input.findEntries("random"); assertEquals(1, entries.size()); diff --git a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java index b77eed1d..364fe495 100644 --- a/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java +++ b/database/src/test/java/org/linguafranca/pwdb/test/Test123Test.java @@ -18,10 +18,7 @@ package org.linguafranca.pwdb.test; import org.junit.jupiter.api.Test; -import org.linguafranca.pwdb.Credentials; -import org.linguafranca.pwdb.Database; -import org.linguafranca.pwdb.Entry; -import org.linguafranca.pwdb.Visitor; +import org.linguafranca.pwdb.*; import java.io.IOException; import java.io.InputStream; @@ -58,7 +55,7 @@ default void test123File() throws ParseException, IOException { Database database; try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(getFileName())) { assertNotNull(inputStream); - database = db.loadDatabase(db.getCredentials("123".getBytes()), inputStream); + database = db.loadDatabase(db.getCredentials(KdbConsts.PASSWORD_BYTES), inputStream); } // visit all groups and entries and list them to console database.visit(new Visitor.Print(getTestPrintStream())); diff --git a/database/src/test/java/org/linguafranca/util/TestUtil.java b/database/src/test/java/org/linguafranca/util/TestUtil.java index 7d5eb078..7778f021 100644 --- a/database/src/test/java/org/linguafranca/util/TestUtil.java +++ b/database/src/test/java/org/linguafranca/util/TestUtil.java @@ -58,14 +58,12 @@ public void write(int b) throws IOException {} /** * Get a print stream that will not close System.out, but will throw away all output - * if "inhibitConsoleOutput" system property has been set - e.g. as a result of using - * mvn clean install -P inhibitConsoleOutput. + * if "enableConsoleOutput" system property has been not set - e.g. as a result of using + * mvn clean install -P enableConsoleOutput. */ public static PrintStream getTestPrintStream() { - return Boolean.getBoolean("inhibitConsoleOutput") ? - // throw away all output - new PrintStream(new NullOutputStream()) : - // never close System.out - new PrintStream(new UncloseableOutputStream(System.out)); + return !Boolean.getBoolean("enableConsoleOutput") + ? new PrintStream(new UncloseableOutputStream(System.out)) + : new PrintStream(new NullOutputStream()); } } \ No newline at end of file diff --git a/example/pom.xml b/example/pom.xml index 1da64fd3..86f155c9 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -19,7 +19,7 @@ KeePassJava2.parent - org.linguafranca.pwdb + mh.keepass 3.0.0-SNAPSHOT ../pom.xml @@ -35,15 +35,10 @@ - org.linguafranca.pwdb + mh.keepass KeePassJava2 ${project.version} - - io.github.novacrypto - SecureString - 2022.01.17 - org.slf4j slf4j-api @@ -53,7 +48,7 @@ logback-classic - org.linguafranca.pwdb + mh.keepass database ${project.version} test-jar diff --git a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java b/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java deleted file mode 100644 index 8b2708f0..00000000 --- a/example/src/main/java/org/linguafranca/pwdb/example/SecureCharBufferPropertyValue.java +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2025. Jo Rabin - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package org.linguafranca.pwdb.example; - -import io.github.novacrypto.SecureCharBuffer; -import org.linguafranca.pwdb.PropertyValue; - -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.StandardCharsets; - -/** - * This is an example of a PropertyValue implementation that uses {@link SecureCharBuffer} - * to store the value in memory. - */ -public class SecureCharBufferPropertyValue implements PropertyValue { - private final SecureCharBuffer value; - - /** - * Builder for {@link SecureCharBufferPropertyValue} - */ - static class Factory implements PropertyValue.Factory { - - @Override - public SecureCharBufferPropertyValue of(CharSequence aString) { - return new SecureCharBufferPropertyValue(aString); - } - - @Override - public SecureCharBufferPropertyValue of(char[] value) { - return new SecureCharBufferPropertyValue(value); - } - - @Override - public SecureCharBufferPropertyValue of(byte[] value) { - return new SecureCharBufferPropertyValue(value); - } - } - - public SecureCharBufferPropertyValue(CharSequence aString) { - this.value = SecureCharBuffer.withCapacity(aString.length()); - this.value.append(aString); - } - - public SecureCharBufferPropertyValue(char [] value) { - this.value = SecureCharBuffer.withCapacity(value.length); - this.value.append(CharBuffer.wrap(value)); - } - - public SecureCharBufferPropertyValue(byte [] value) { - this.value = SecureCharBuffer.withCapacity(value.length); - this.value.append(StandardCharsets.UTF_8.decode(ByteBuffer.wrap(value))); - } - @Override - public String getValueAsString() { - return this.value.toStringAble().toString(); - } - - @Override - public CharSequence getValue() { - return this.value; - } - - @Override - public char [] getValueAsChars() { - CharBuffer cb = CharBuffer.wrap(value); - char[] result = new char[cb.limit()]; - cb.get(result); - return result; - } - - @Override - public byte [] getValueAsBytes() { - CharBuffer cb = CharBuffer.wrap(value); - ByteBuffer bb = StandardCharsets.UTF_8.encode(cb); - byte[] result = new byte[bb.limit()]; - bb.get(result); - return result; - } - - @Override - public boolean isProtected() { - return true; - } -} - diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index b9177d87..e3bb0fc4 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -18,6 +18,7 @@ package org.linguafranca.pwdb.kdbx; import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.Visitor; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; @@ -51,7 +52,7 @@ public Database load(KdbxCredentials creds, InputStream inputStream) throws Exce } private static void testDb (DbLoader loader, String label, int loads, int iterations) throws Exception { - KdbxCredentials creds = new KdbxCredentials("123".getBytes()); + KdbxCredentials creds = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); long start = System.currentTimeMillis(); for (int i=0; i < loads; i++) { try (InputStream inputStream = OpenDbExample.class.getClassLoader().getResourceAsStream("test1.kdbx")) { diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java index 643fac02..be04fb46 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/QuickStart.java @@ -30,10 +30,12 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardOpenOption; import java.util.List; +import java.util.Objects; +import static java.nio.file.StandardOpenOption.*; import static org.linguafranca.pwdb.Entry.STANDARD_PROPERTY_NAME.*; -import static org.linguafranca.util.TestUtil.getTestPrintStream; /** * Examples for QuickStart @@ -41,12 +43,9 @@ * @author jo */ @SuppressWarnings("WeakerAccess") -public abstract class QuickStart { +public final class QuickStart { - public static final String TEST_OUTPUT_DIR = "testOutput/"; - static PrintStream printStream = getTestPrintStream(); - - public void canonicalQuickStart() throws IOException { + public static void canonicalQuickStart(Path tempPath) throws IOException { // V3 create a database, no need to specify what kind (e.g. JacksonDatabase) // V3 Database no longer need to be generified @@ -63,18 +62,18 @@ public void canonicalQuickStart() throws IOException { // V3 abbreviated static import of standard field names .addProperty(TITLE, "first entry") .addProperty(USER_NAME, "Tom") - .addProperty(PASSWORD, "123".getBytes()) + .addProperty(PASSWORD, KdbConsts.PASSWORD_BYTES) .addProperty(URL, "http://localhost:8080/") .addEntry("2nd Entry") // v3 addEntry on Entry adds Entry to parent Group if exists .addProperty(USER_NAME, "Alice") - .addProperty(PASSWORD, "123".getBytes()); + .addProperty(PASSWORD, KdbConsts.PASSWORD_BYTES); // find entries just added, in V3 the findEntries return value is just List List entries = database.findEntries("entry"); // V3 KdbxCreds now called KdbxCredentials - KdbxCredentials credentials = new KdbxCredentials("123".getBytes()); - try (OutputStream outputStream = Files.newOutputStream(Path.of(TEST_OUTPUT_DIR, "test.kdbx"))) { + KdbxCredentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); + try (OutputStream outputStream = Files.newOutputStream(tempPath.resolve("test.kdbx"))) { database.save(credentials, outputStream); } } @@ -82,7 +81,7 @@ public void canonicalQuickStart() throws IOException { /** * Illustrates the fluent API for creating a database, as well as the various ways of setting property values. */ - public void fluentExample() { + public static void fluentExample() { Database database = new KdbxDatabase(); database.getRootGroup().addGroup("Group 1") .addEntry("as property values determined by the database property value strategy (preferred)") @@ -107,15 +106,15 @@ public void fluentExample() { /** * Load KDBX from file */ - public void loadKdbx() throws IOException { + public static void loadKdbx() throws IOException { // get an input stream - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx")) { + try (InputStream inputStream = QuickStart.class.getResourceAsStream("/test123.kdbx")) { // password credentials - Credentials credentials = new KdbxCredentials("123".getBytes()); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); // load the database from the input stream - Database database = KdbxDatabase.load(credentials, inputStream); + Database database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); // visit all groups and entries and list them to console - database.visit(new Visitor.Print(printStream)); + database.visit(new Visitor.Print(System.out)); } } @@ -123,11 +122,11 @@ public void loadKdbx() throws IOException { * Save KDBX to file */ - private Entry entryFactory(Database database, String s, int e) { + private static Entry entryFactory(Database database, String s, int e) { return database.newEntry(String.format("Group %s Entry %d", s, e)); } - public void saveKdbx() throws IOException { + public static void saveKdbx(Path tempPath) throws IOException { // create an empty database Database database = new KdbxDatabase(); @@ -141,15 +140,15 @@ public void saveKdbx() throws IOException { } // save to a file with password "123" - try (FileOutputStream outputStream = new FileOutputStream(TEST_OUTPUT_DIR + "test.kdbx")) { - database.save(new KdbxCredentials("123".getBytes()), outputStream); + try (OutputStream outputStream = Files.newOutputStream(tempPath.resolve( "test.kdbx"))) { + database.save(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), outputStream); } } /** * Splice - add a group from one database to a parent from another (or copy from the same db) */ - public void splice(Group newParent, Group groupToSplice) { + public static void splice(Group newParent, Group groupToSplice) { Group addedGroup = newParent.addGroup(newParent.getDatabase().newGroup(groupToSplice)); addedGroup.copy(groupToSplice); } @@ -157,7 +156,7 @@ public void splice(Group newParent, Group groupToSplice) { /** * Group by title */ - public Group groupByTitle(Database database) { + public static Group groupByTitle(Database database) { List entries = database.findEntries(entry -> entry.getProperty(Entry.STANDARD_PROPERTY_NAME_TITLE).toLowerCase().contains("findme!")); // create a new group using DB factory method @@ -175,12 +174,14 @@ public Group groupByTitle(Database database) { /** * Load KDB and save as KDBX */ - public void loadKdbSaveKdbx() throws IOException { - InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb"); - // password credentials - Credentials credentials = new KdbCredentials.Password("123".getBytes()); - // load KdbDatabase - KdbDatabase database = KdbDatabase.load(credentials, inputStream); + public static void loadKdbSaveKdbx(Path tempPath) throws IOException { + KdbDatabase database; + try (InputStream inputStream = QuickStart.class.getResourceAsStream("/test123.kdb")) { + // password credentials + Credentials credentials = new KdbCredentials.Password(KdbConsts.PASSWORD_BYTES); + // load KdbDatabase + database = KdbDatabase.load(credentials, Objects.requireNonNull(inputStream)); + } // create a KDBX (database Database kdbxDatabase = new KdbxDatabase(); @@ -189,21 +190,21 @@ public void loadKdbSaveKdbx() throws IOException { // deep copy from group (not including source group, KDB database has simulated root) kdbxDatabase.getRootGroup().copy(database.getRootGroup()); // save it - try (FileOutputStream f = new FileOutputStream(TEST_OUTPUT_DIR + "migration.kdbx")) { - kdbxDatabase.save(new KdbxCredentials("123".getBytes()), f); + try (OutputStream f = Files.newOutputStream(tempPath.resolve( "migration.kdbx"))) { + kdbxDatabase.save(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), f); } } /** * Load KDBX V3 save as KDBX V4 */ - public void loadKdbx3SaveKdbx4(String v3ResourceName, byte[] password, Path v4Filename) throws IOException { + public static void loadKdbx3SaveKdbx4(String v3ResourceName, byte[] password, Path v4Filename) throws IOException { KdbxDatabase database; // password credentials KdbxCredentials credentials = new KdbxCredentials(password); - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(v3ResourceName)) { + try (InputStream inputStream = QuickStart.class.getResourceAsStream(v3ResourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); } // create a KDBX (database @@ -223,22 +224,21 @@ public void loadKdbx3SaveKdbx4(String v3ResourceName, byte[] password, Path v4Fi KdbxStreamFormat formatV4 = new KdbxStreamFormat(kdbxHeader); // save it with format options - try (OutputStream v4OutputStream = Files.newOutputStream(v4Filename)) { + try (OutputStream v4OutputStream = Files.newOutputStream(v4Filename, CREATE_NEW, SYNC)) { kdbxDatabase.save(formatV4, credentials, v4OutputStream); } } - /** * Load KDBX V3 save as KDBX V4 */ - public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3Filename) throws IOException { + public static void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3Filename) throws IOException { KdbxDatabase database; // password credentials KdbxCredentials credentials = new KdbxCredentials(password); - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { + try (InputStream inputStream = QuickStart.class.getResourceAsStream(resourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); } // create a KDBX database @@ -261,13 +261,13 @@ public void loadKdbx4SaveKdbx3(String resourceName, byte[] password, Path v3File /** * Load KDBX V3 save as KDBX V4 */ - public void loadKdbx4SaveToConsole(String resourceName, byte[] password, Path path) throws IOException { + public static void loadKdbx4SaveToConsole(String resourceName, byte[] password, Path path) throws IOException { KdbxDatabase database; // password credentials KdbxCredentials credentials = new KdbxCredentials(password); - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resourceName)) { + try (InputStream inputStream = QuickStart.class.getResourceAsStream(resourceName)) { // load KdbDatabase - database = KdbxDatabase.load(credentials, inputStream); + database = KdbxDatabase.load(credentials, Objects.requireNonNull(inputStream)); } database.save(new StreamFormat.None(), new Credentials.None(), TestUtil.getTestPrintStream()); diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java index 08883a02..f9c98e19 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SaxParseTest.java @@ -18,6 +18,8 @@ package org.linguafranca.pwdb.kdbx; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.xml.sax.SAXException; @@ -26,22 +28,23 @@ import java.io.OutputStream; import java.io.PrintWriter; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; /** * @author jo */ - +@Execution(CONCURRENT) public class SaxParseTest extends SaxParse { - OutputStream outputStream = getTestPrintStream(); @Test public void exampleSaxParsingV3 () throws IOException, SAXException, ParserConfigurationException { - super.exampleSaxParsing("test123.kdbx", new KdbxCredentials("123".getBytes()), new PrintWriter(outputStream)); + super.exampleSaxParsing("test123.kdbx", new KdbxCredentials(KdbConsts.PASSWORD_BYTES), new PrintWriter(System.out)); } + @Test public void exampleSaxParsingV4 () throws IOException, SAXException, ParserConfigurationException { - super.exampleSaxParsing("V4-AES-AES.kdbx", new KdbxCredentials("123".getBytes()), new PrintWriter(outputStream)); + super.exampleSaxParsing("V4-AES-AES.kdbx", new KdbxCredentials(KdbConsts.PASSWORD_BYTES), new PrintWriter(System.out)); } } diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index c19b1d1a..daadc00a 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -1,5 +1,6 @@ /* * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2025. Sergio Lissner * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,80 +18,93 @@ package org.linguafranca.pwdb.kdbx; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.api.parallel.Execution; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxStreamFormat; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; import org.linguafranca.pwdb.security.Encryption; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; +import static org.linguafranca.pwdb.kdbx.QuickStart.*; /** * Simple illustration of hooking a SAX parser up to process a KDBX file * * @author jo */ -public class SimpleQuickStartTest extends QuickStart { - - - @BeforeAll - public static void ensureOutputDir() throws IOException { - Files.createDirectories(Paths.get(TEST_OUTPUT_DIR)); - } +@Execution(CONCURRENT) +public class SimpleQuickStartTest { @Test - public void canonicalQuickstartTest() throws IOException { - super.canonicalQuickStart(); + public void canonicalQuickstartTest(@TempDir Path tempPath) throws IOException { + canonicalQuickStart(tempPath); } @Test - public void fluentExampleTest() throws IOException { - super.fluentExample(); + public void fluentExampleTest() { + fluentExample(); } @Test - public void saveTest() throws IOException { - super.saveKdbx(); + public void saveTest(@TempDir Path tempPath) throws IOException { + saveKdbx(tempPath); } @Test public void loadTest() throws IOException { - super.loadKdbx(); + QuickStart.loadKdbx(); } @Test - public void loadKdbSaveKdbx() throws IOException { - super.loadKdbSaveKdbx(); + public void loadKdbSaveKdbx(@TempDir Path tempPath) throws IOException { + QuickStart.loadKdbSaveKdbx(tempPath); } @Test - public void loadSave() throws IOException { - Path path = Paths.get("testOutput/CHACHA-AES-CHACHA.kdbx"); + public void loadSave(@TempDir Path tempPath) throws IOException { + Path actualTempPath = Files.createTempDirectory("kbdx-"); + Path path = actualTempPath.resolve("CHACHA-AES-CHACHA.kdbx"); - loadKdbx3SaveKdbx4("test123.kdbx","123".getBytes(), path); + QuickStart.loadKdbx3SaveKdbx4("/test123.kdbx", KdbConsts.PASSWORD_BYTES, path); // load newly created V4 database - KdbxDatabase db = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), Files.newInputStream(path)); + KdbxDatabase db; + try (InputStream inputStream = Files.newInputStream(path)) { + db = KdbxDatabase.load(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), inputStream); + } + catch (Throwable t) { + System.out.println(path.toAbsolutePath()); + int _=0; + throw new RuntimeException(t); + } KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(4, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.CHA_CHA_20, streamFormat.getStreamConfiguration().getCipherAlgorithm()); assertEquals(Encryption.KeyDerivationFunction.ARGON2, streamFormat.getStreamConfiguration().getKeyDerivationFunction()); assertEquals(Encryption.ProtectedStreamAlgorithm.CHA_CHA_20, streamFormat.getStreamConfiguration().getProtectedStreamAlgorithm()); } + @Test - public void loadSave2() throws IOException { - Path path = Paths.get("testOutput/CHACHA-AES-CHACHA.kdbx"); + public void loadSave2(@TempDir Path tempPath) throws IOException { + Path path = tempPath.resolve("CHACHA-AES-CHACHA.kdbx"); - loadKdbx4SaveKdbx3("V4-ChaCha20-Argon2-Attachment.kdbx","123".getBytes(), path); + QuickStart.loadKdbx4SaveKdbx3("/V4-ChaCha20-Argon2-Attachment.kdbx", KdbConsts.PASSWORD_BYTES, path); // load newly created V4 database - KdbxDatabase db = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), Files.newInputStream(path)); + KdbxDatabase db; + try (InputStream inputStream = Files.newInputStream(path)) { + db = KdbxDatabase.load(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), inputStream); + } KdbxStreamFormat streamFormat = (KdbxStreamFormat) db.getStreamFormat(); assertEquals(3, streamFormat.getStreamConfiguration().getVersion()); assertEquals(Encryption.Cipher.AES, streamFormat.getStreamConfiguration().getCipherAlgorithm()); diff --git a/kdb/pom.xml b/kdb/pom.xml index 5d2cb753..cab07560 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -19,7 +19,7 @@ KeePassJava2.parent - org.linguafranca.pwdb + mh.keepass 3.0.0-SNAPSHOT ../pom.xml @@ -31,13 +31,13 @@ - org.linguafranca.pwdb + mh.keepass database ${project.version} - org.linguafranca.pwdb + mh.keepass database ${project.version} test-jar diff --git a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java index 4797eb31..3929703e 100644 --- a/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java +++ b/kdb/src/test/java/org/linguafranca/pwdb/kdb/KdbDatabaseTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Database; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.Visitor; import org.linguafranca.pwdb.test.DatabaseTestBase; import org.linguafranca.pwdb.test.GroupsAndEntriesTest; @@ -70,7 +71,7 @@ public void testCreateKdbDatabase() throws Exception { Database database; try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdb")) { assertNotNull(inputStream); - database = KdbDatabase.load(new KdbCredentials.Password("123".getBytes()), inputStream); + database = KdbDatabase.load(new KdbCredentials.Password(KdbConsts.PASSWORD_BYTES), inputStream); } database.visit(new Visitor.Print(getTestPrintStream())); } @@ -80,7 +81,7 @@ public void openKdbWithKeyFile() throws IOException { KdbCredentials creds; try (InputStream key = getClass().getClassLoader().getResourceAsStream("kdb.key")) { assertNotNull(key); - creds = new KdbCredentials.KeyFile("123".getBytes(), key); + creds = new KdbCredentials.KeyFile(KdbConsts.PASSWORD_BYTES, key); } KdbDatabase db; try (InputStream is = getClass().getClassLoader().getResourceAsStream("kdbwithkey.kdb")) { diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index 11406615..13ce7b54 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -18,7 +18,7 @@ KeePassJava2.parent - org.linguafranca.pwdb + mh.keepass 3.0.0-SNAPSHOT ../pom.xml @@ -30,22 +30,17 @@ - org.linguafranca.pwdb + mh.keepass KeePassJava2.kdbx.io ${project.version} com.fasterxml.jackson.dataformat jackson-dataformat-xml - 2.21.1 + 2.21.2 - com.fasterxml.woodstox - woodstox-core - 7.1.1 - - - org.linguafranca.pwdb + mh.keepass database ${project.version} test-jar diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index b813c648..dedd9106 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -57,15 +57,13 @@ public class KdbxSerializableDatabase implements SerializableDatabase { public static KeePassFile createEmptyDatabase() { - InputStream inputStream = KdbxSerializableDatabase.class.getClassLoader() - .getResourceAsStream("base.kdbx.xml"); - XmlMapper mapper = new XmlMapper(); - try { + try (InputStream inputStream = KdbxSerializableDatabase.class.getClassLoader().getResourceAsStream("base.kdbx.xml")) { + XmlMapper mapper = new XmlMapper(); return mapper.readValue(inputStream, KeePassFile.class); - } catch (IOException e) { + } + catch (IOException e) { throw new RuntimeException(e); } - } public KdbxSerializableDatabase() { @@ -106,7 +104,7 @@ public void save(OutputStream outputStream) { mapper.enable(ToXmlGenerator.Feature.WRITE_XML_DECLARATION); mapper.enable(SerializationFeature.INDENT_OUTPUT); mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS); - mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + mapper.setDefaultPropertyInclusion(JsonInclude.Include.NON_EMPTY); // set the serializer to Woodstox System.setProperty("javax.xml.stream.XMLOutputFactory", "com.ctc.wstx.stax.WstxOutputFactory"); diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java index 55a5f3ff..72577c9b 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/converter/StringToDateConverter.java @@ -19,6 +19,7 @@ import java.util.Date; import java.util.Objects; +import org.jspecify.annotations.Nullable; import org.linguafranca.pwdb.format.Helpers; import com.fasterxml.jackson.databind.util.StdConverter; @@ -26,7 +27,7 @@ public class StringToDateConverter extends StdConverter { @Override - public Date convert(String value) { + public @Nullable Date convert(String value) { // TODO: It would really be better if we could inhibit deserialize date elements that are not present if (Objects.isNull(value) || value.isEmpty()) { return null; diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java index 0bb4c68a..4557abe0 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/util/Util.java @@ -29,7 +29,9 @@ import java.io.*; import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Objects; +@SuppressWarnings("resource") public class Util { List> implementations = List.of(KdbxDatabase.class); @@ -46,8 +48,9 @@ public static InputStream getDecryptedInputStream (String resourceName, Credenti } public static InputStream getDecryptedInputStream (String resourceName, Credentials credentials, KdbxHeader header) throws IOException { - InputStream is = Util.class.getClassLoader().getResourceAsStream(resourceName); - return KdbxSerializer.createUnencryptedInputStream(credentials, header, is); + // Don't close inputStream here + InputStream is = Util.class.getResourceAsStream(resourceName); + return KdbxSerializer.createUnencryptedInputStream(credentials, header, Objects.requireNonNull(is, ()->"resource: "+resourceName)); } public static String streamToString(InputStream inputStream) throws IOException { @@ -59,9 +62,8 @@ public static String streamToString(InputStream inputStream) throws IOException */ public static void listDatabase(String resourceName, Credentials creds, OutputStream outputStream) throws IOException { KdbxDatabase database; - try (InputStream inputStream = Util.class.getClassLoader().getResourceAsStream(resourceName)) { - database = KdbxDatabase.load(creds, inputStream); - } + InputStream inputStream = Util.class.getResourceAsStream(resourceName); + database = KdbxDatabase.load(creds, Objects.requireNonNull(inputStream, ()->"resource: "+resourceName)); database.save(new StreamFormat.None(), new KdbxCredentials.None(), outputStream); } @@ -70,9 +72,8 @@ public static void listDatabase(String resourceName, Credentials creds, OutputSt */ public static void listDatabase(DatabaseLoader loader, String resourceName, Credentials creds, OutputStream outputStream) throws IOException { Database database; - try (InputStream inputStream = Util.class.getClassLoader().getResourceAsStream(resourceName)) { - database = loader.load(creds, inputStream); - } + InputStream inputStream = Util.class.getResourceAsStream(resourceName); + database = loader.load(creds, Objects.requireNonNull(inputStream, ()->"resource: "+resourceName)); database.save(new StreamFormat.None(), new KdbxCredentials.None(), outputStream); outputStream.flush(); } @@ -100,8 +101,9 @@ public static void listXml(String resourceName, Credentials credentials, PrintWr public static void listKdbxHeaderProperties(String resourceName, PrintWriter printWriter) throws IOException { printWriter.format(resourceName + "\n"); KdbxHeader kdbxHeader = new KdbxHeader(); - InputStream is = Util.class.getClassLoader().getResourceAsStream(resourceName); - KdbxSerializer.readOuterHeader(is, kdbxHeader); + try (InputStream is = Util.class.getResourceAsStream(resourceName)) { + KdbxSerializer.readOuterHeader(Objects.requireNonNull(is, ()->"resource: "+resourceName), kdbxHeader); + } printWriter.format("Version: %d\n", kdbxHeader.getVersion()); printWriter.format("Cipher Algorithm: %s\n", kdbxHeader.getCipherAlgorithm().getName()); printWriter.format("Key Derivation Function: %s\n", kdbxHeader.getKeyDerivationFunction().getName()); diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java index e23ed11c..8bed2876 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxBinariesTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.parallel.Execution; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.test.BinaryPropertiesTest; import org.linguafranca.pwdb.test.DatabaseTestBase; @@ -29,7 +30,7 @@ public class KdbxBinariesTest { public static class KdbxBinariesV4Test extends KdbxTestBase implements BinaryPropertiesTest { KdbxBinariesV4Test() { DatabaseTestBase db = getDbFunc().get(); - db.loadAndInitDatabase("123".getBytes(), "V4-ChaCha20-Argon2-Attachment.kdbx"); + db.loadAndInitDatabase(KdbConsts.PASSWORD_BYTES, "V4-ChaCha20-Argon2-Attachment.kdbx"); } } @@ -37,7 +38,7 @@ public static class KdbxBinariesV4Test extends KdbxTestBase implements BinaryPro public static class KdbxBinariesV3Test extends KdbxTestBase implements BinaryPropertiesTest { KdbxBinariesV3Test() { DatabaseTestBase db = getDbFunc().get(); - db.loadAndInitDatabase("123".getBytes(), "Attachment.kdbx"); + db.loadAndInitDatabase(KdbConsts.PASSWORD_BYTES, "Attachment.kdbx"); } } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java index d0326287..875bf946 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/KdbxDatabaseLoadTest.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.Visitor; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; @@ -37,7 +38,7 @@ public class KdbxDatabaseLoadTest { public void loadXml() throws Exception { PrintStream printStream = getTestPrintStream(); KdbxDatabase database; - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("ExampleDatabase.xml")) { + try (InputStream inputStream = KdbxDatabaseLoadTest.class.getResourceAsStream("/ExampleDatabase.xml")) { assertNotNull(inputStream); database = KdbxDatabase.loadXml(inputStream); } @@ -47,9 +48,9 @@ public void loadXml() throws Exception { public void loadKdbx() throws Exception { PrintStream printStream = getTestPrintStream(); KdbxDatabase database; - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("test123.kdbx")) { + try (InputStream inputStream = KdbxDatabaseLoadTest.class.getResourceAsStream("/test123.kdbx")) { assertNotNull(inputStream); - database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + database = KdbxDatabase.load(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), inputStream); } database.visit(new Visitor.Print(printStream)); } @@ -58,9 +59,9 @@ public void loadKdbx() throws Exception { public void loadKdbxV4() throws Exception { PrintStream printStream = getTestPrintStream(); KdbxDatabase database; - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-AES-Argon2.kdbx")) { + try (InputStream inputStream = KdbxDatabaseLoadTest.class.getResourceAsStream("/V4-AES-Argon2.kdbx")) { assertNotNull(inputStream); - database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + database = KdbxDatabase.load(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), inputStream); } database.visit(new Visitor.Print(printStream)); // test what happens to dates in V4 @@ -83,9 +84,9 @@ public void emptyDb() throws Exception { public void dbWithDeleted() throws Exception { PrintStream printStream = getTestPrintStream(); KdbxDatabase database; - try (InputStream inputStream = getClass().getClassLoader().getResourceAsStream("testDeleted.kdbx")) { + try (InputStream inputStream = KdbxDatabaseLoadTest.class.getResourceAsStream("/testDeleted.kdbx")) { assertNotNull(inputStream); - database = KdbxDatabase.load(new KdbxCredentials("123".getBytes()), inputStream); + database = KdbxDatabase.load(new KdbxCredentials(KdbConsts.PASSWORD_BYTES), inputStream); } database.visit(new Visitor.Print(printStream)); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/package-info.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/package-info.java new file mode 100644 index 00000000..6143e46b --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@NullMarked +package org.linguafranca.pwdb.kdbx.database; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java index 61b88fb3..5803b0e4 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/UtilTest.java @@ -19,35 +19,40 @@ import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.parallel.Execution; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; +import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.pwdb.kdbx.jackson.util.Util.*; import static org.linguafranca.util.TestUtil.getTestPrintStream; +@Execution(CONCURRENT) public class UtilTest { - OutputStream outputStream = getTestPrintStream(); - @Test public void listDatabaseTest() throws IOException { - listDatabase("V3-CustomIcon.kdbx", new KdbxCredentials("123".getBytes()), outputStream); + OutputStream outputStream = getTestPrintStream(); + listDatabase("/V3-CustomIcon.kdbx", new KdbxCredentials(KdbConsts.PASSWORD_BYTES), outputStream); } @Test public void listXmlTest() throws IOException { + OutputStream outputStream = getTestPrintStream(); PrintWriter writer = new PrintWriter(outputStream); - listXml("Database-4.1-123.kdbx", new KdbxCredentials("123".getBytes()), writer); + listXml("/Database-4.1-123.kdbx", new KdbxCredentials(KdbConsts.PASSWORD_BYTES), writer); writer.flush(); } @Test public void listXmlTest2() throws IOException { + OutputStream outputStream = getTestPrintStream(); PrintWriter writer = new PrintWriter(outputStream); - listXml("issue-70/test2.kdbx", new KdbxCredentials("KeePassJava2".getBytes()), writer); + listXml("/issue-70/test2.kdbx", new KdbxCredentials("KeePassJava2".getBytes()), writer); writer.flush(); } /** @@ -55,20 +60,22 @@ public void listXmlTest2() throws IOException { */ @Test public void listKdbxHeaderParams () throws IOException { + OutputStream outputStream = getTestPrintStream(); PrintWriter writer = new PrintWriter(outputStream); - listKdbxHeaderProperties("test123.kdbx", writer); - listKdbxHeaderProperties("V4-AES-AES.kdbx", writer); - listKdbxHeaderProperties("V4-AES-Argon2.kdbx", writer); - listKdbxHeaderProperties("V4-ChaCha20-AES.kdbx", writer); - listKdbxHeaderProperties("V4-ChaCha20-Argon2-Attachment.kdbx", writer); + listKdbxHeaderProperties("/test123.kdbx", writer); + listKdbxHeaderProperties("/V4-AES-AES.kdbx", writer); + listKdbxHeaderProperties("/V4-AES-Argon2.kdbx", writer); + listKdbxHeaderProperties("/V4-ChaCha20-AES.kdbx", writer); + listKdbxHeaderProperties("/V4-ChaCha20-Argon2-Attachment.kdbx", writer); writer.flush(); } @Test public void listHeaderPropertiesAndXml() throws IOException { + OutputStream outputStream = getTestPrintStream(); PrintWriter writer = new PrintWriter(outputStream); - listKdbxHeaderProperties("V4-AES-Argon2-CustomIcon.kdbx", writer); - listXml("V4-AES-Argon2-CustomIcon.kdbx", new KdbxCredentials("123".getBytes()), writer); + listKdbxHeaderProperties("/V4-AES-Argon2-CustomIcon.kdbx", writer); + listXml("/V4-AES-Argon2-CustomIcon.kdbx", new KdbxCredentials(KdbConsts.PASSWORD_BYTES), writer); writer.flush(); } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/package-info.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/package-info.java new file mode 100644 index 00000000..120221c1 --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/util/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@NullMarked +package org.linguafranca.pwdb.kdbx.database.util; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java index 1019eef2..4bc3d604 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue27Test.java @@ -21,6 +21,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; @@ -35,6 +36,7 @@ import java.util.List; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -48,9 +50,12 @@ public class Issue27Test { public void testIssue27() throws IOException { PrintStream printStream = getTestPrintStream(); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); - KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); - KdbxDatabase db = KdbxDatabase.load(creds, is); + KdbxDatabase db; + try (InputStream is = Issue27Test.class.getResourceAsStream("/issue-27/bogus-timestamp2.kdbx")) { + assertNotNull(is); + KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); + db = KdbxDatabase.load(creds, is); + } List entries = db.findEntries("testtitle"); for (Entry entry: entries) { @@ -66,9 +71,12 @@ public void testIssue27() throws IOException { public void testV4Date() throws IOException { PrintStream printStream = getTestPrintStream(); - InputStream is = this.getClass().getClassLoader().getResourceAsStream("V4-AES-AES.kdbx"); - KdbxCredentials creds = new KdbxCredentials("123".getBytes()); - KdbxDatabase db = KdbxDatabase.load(creds, is); + KdbxDatabase db; + try (InputStream is = Issue27Test.class.getResourceAsStream("/V4-AES-AES.kdbx")) { + assertNotNull(is); + KdbxCredentials creds = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); + db = KdbxDatabase.load(creds, is); + } List entries = db.findEntries("Sample Entry #2 - Copy"); for (Entry entry: entries) { @@ -82,10 +90,13 @@ public void testIssue27XML() throws IOException { PrintStream printStream = getTestPrintStream(); - InputStream is = Issue27Test.class.getClassLoader().getResourceAsStream("issue-27/bogus-timestamp2.kdbx"); - KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); - InputStream plainText = KdbxSerializer.createUnencryptedInputStream(creds,new KdbxHeader(), is); - String string = IOUtils.toString(plainText, StandardCharsets.UTF_8); - printStream.println(string); + InputStream plainText; + try (InputStream is = Issue27Test.class.getResourceAsStream("/issue-27/bogus-timestamp2.kdbx")) { + assertNotNull(is); + KdbxCredentials creds = new KdbxCredentials("passwordless".getBytes()); + plainText = KdbxSerializer.createUnencryptedInputStream(creds, new KdbxHeader(), is); + String string = IOUtils.toString(plainText, StandardCharsets.UTF_8); + printStream.println(string); + } } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java index 113b9c70..5617785e 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue33Test.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.StreamFormat; import org.linguafranca.pwdb.format.Helpers; import org.linguafranca.pwdb.format.KdbxCredentials; @@ -30,6 +31,7 @@ import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.nio.file.Files; import java.nio.file.Path; @@ -43,14 +45,18 @@ @Execution(CONCURRENT) public class Issue33Test { - public static final String TEST_RESOURCE = "V4-AES-Argon2-CustomIcon.kdbx"; - public static final KdbxCredentials CREDENTIALS = new KdbxCredentials("123".getBytes()); + public static final String TEST_RESOURCE = "/V4-AES-Argon2-CustomIcon.kdbx"; + public static final KdbxCredentials CREDENTIALS = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); public static void listXml(Path tempPath) throws IOException { final Path TEST_XML_FILE = tempPath.resolve("Issue33Source.xml"); - Util.listXml(TEST_RESOURCE, CREDENTIALS, new PrintWriter(System.out)); + if (System.out==null) { + throw new IllegalStateException("(System.out==null)"); + } + PrintWriter printWriter = new PrintWriter(System.out); + Util.listXml(TEST_RESOURCE, CREDENTIALS, printWriter); Helpers.isV4.set(true); } @@ -59,10 +65,12 @@ public void testJacksonDatabase(@TempDir Path tempPath) throws IOException { listXml(tempPath); KdbxDatabase database; - try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE)) { + try (InputStream inputStream = Issue33Test.class.getResourceAsStream(TEST_RESOURCE)) { assertNotNull(inputStream); database = KdbxDatabase.load(CREDENTIALS, inputStream); } - database.save(new StreamFormat.None(), new Credentials.None(), Files.newOutputStream(tempPath.resolve("Issue33Jackson.xml"))); + try (OutputStream outputStream = Files.newOutputStream(tempPath.resolve("Issue33Jackson.xml"))) { + database.save(new StreamFormat.None(), new Credentials.None(), outputStream); + } } } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java index e383da16..b7505730 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue38Test.java @@ -28,6 +28,7 @@ import java.io.PrintStream; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; import static org.linguafranca.util.TestUtil.getTestPrintStream; @@ -41,12 +42,16 @@ public class Issue38Test { public void testV2Keyfile() throws IOException { PrintStream printStream = getTestPrintStream(); - InputStream databaseStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database.kdbx"); - InputStream keyStream = Issue38Test.class.getClassLoader().getResourceAsStream("issue-38/Database.keyx"); - assert keyStream != null; - KdbxCredentials creds = new KdbxCredentials("MyPassword".getBytes(), keyStream); - assert databaseStream != null; - KdbxDatabase database = KdbxDatabase.load(creds, databaseStream); + KdbxDatabase database; + try (InputStream databaseStream = Issue38Test.class.getResourceAsStream("/issue-38/Database.kdbx")) { + KdbxCredentials creds; + try (InputStream keyStream = Issue38Test.class.getResourceAsStream("/issue-38/Database.keyx")) { + assertNotNull(keyStream); + creds = new KdbxCredentials("MyPassword".getBytes(), keyStream); + } + assertNotNull(databaseStream); + database = KdbxDatabase.load(creds, databaseStream); + } List entries = database.findEntries("Sample Entry"); Entry entry = entries.get(0); printStream.println(entry.getTitle()); diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java index f24c15e2..51285e4f 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue70Test.java @@ -19,6 +19,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.parallel.Execution; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.kdbx.jackson.KdbxDatabase; @@ -34,18 +35,18 @@ @Execution(CONCURRENT) public class Issue70Test { - public static final String TEST_RESOURCE1 = "issue-70/test2.kdbx"; + public static final String TEST_RESOURCE1 = "/issue-70/test2.kdbx"; public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials("KeePassJava2".getBytes()); - public static final String TEST_RESOURCE2 = "Database-4.1-123.kdbx"; - public static final KdbxCredentials CREDENTIALS2 = new KdbxCredentials("123".getBytes()); - public static final String TEST_RESOURCE3 = "issue-70/test-hugoo10.kdbx"; + public static final String TEST_RESOURCE2 = "/Database-4.1-123.kdbx"; + public static final KdbxCredentials CREDENTIALS2 = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); + public static final String TEST_RESOURCE3 = "/issue-70/test-hugoo10.kdbx"; public static final KdbxCredentials CREDENTIALS3 = new KdbxCredentials("test".getBytes()); /* custom data missing from meta */ @Test public void testCustomData() throws IOException { - try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + try (InputStream inputStream = Issue70Test.class.getResourceAsStream(TEST_RESOURCE1)) { assertNotNull(inputStream); KdbxDatabase database = KdbxDatabase.load(CREDENTIALS1, inputStream); } @@ -53,7 +54,7 @@ public void testCustomData() throws IOException { @Test public void testFileFormat_4_1() throws IOException { - try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE2)){ + try (InputStream inputStream = Issue70Test.class.getResourceAsStream(TEST_RESOURCE2)){ assertNotNull(inputStream); KdbxDatabase database = KdbxDatabase.load(CREDENTIALS2, inputStream); } @@ -62,7 +63,7 @@ public void testFileFormat_4_1() throws IOException { /* custom data missing from entry definition */ @Test public void testCustomData2() throws IOException { - try (InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(TEST_RESOURCE3)){ + try (InputStream inputStream = Issue70Test.class.getResourceAsStream(TEST_RESOURCE3)){ assertNotNull(inputStream); KdbxDatabase database = KdbxDatabase.load(CREDENTIALS3, inputStream); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java index 5d1b088e..3db11046 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue87Test.java @@ -1,8 +1,10 @@ package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.parallel.Execution; import org.linguafranca.pwdb.Entry; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -29,14 +31,14 @@ @Execution(CONCURRENT) public class Issue87Test { - public static final String TEST_RESOURCE1 = "issue-87/customIcon-123.kdbx"; - public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials("123".getBytes()); - public static final String TEST_OUTPUT_ISSUE_87_KDBX = "testOutput/Issue87.kdbx"; + public static final String TEST_RESOURCE1 = "/issue-87/customIcon-123.kdbx"; + public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); + public static final String TEST_OUTPUT_ISSUE_87_KDBX = "Issue87.kdbx"; @Test - public void testCustomIcon() throws IOException { + public void testCustomIcon(@TempDir Path tempPath) throws IOException { KdbxDatabase database; - try (InputStream inputStream = Issue87Test.class.getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + try (InputStream inputStream = Issue87Test.class.getResourceAsStream(TEST_RESOURCE1)) { assertNotNull(inputStream); database = KdbxDatabase.load(CREDENTIALS1, inputStream); } @@ -50,7 +52,7 @@ public void testCustomIcon() throws IOException { System.out.println("Custom icon id: " + customIcon); KeePassFile.Icon icon = entry.getCustomIcon(); - Path path = Paths.get(TEST_OUTPUT_ISSUE_87_KDBX); + Path path = tempPath.resolve(TEST_OUTPUT_ISSUE_87_KDBX); try (OutputStream outputStream = Files.newOutputStream(path)) { database.save(CREDENTIALS1, outputStream); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java index 6000fe0d..a451f1af 100644 --- a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/Issue88Test.java @@ -1,7 +1,9 @@ package org.linguafranca.pwdb.kdbx.database.validation; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.api.parallel.Execution; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -24,21 +26,19 @@ @Execution(CONCURRENT) public class Issue88Test { - - public static final String TEST_RESOURCE1 = "issue-88/newDb-123.kdbx"; - public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials("123".getBytes()); - public static final String TEST_OUTPUT_ISSUE_88_KDBX = "testOutput/Issue88.kdbx"; - + public static final String TEST_RESOURCE1 = "/issue-88/newDb-123.kdbx"; + public static final KdbxCredentials CREDENTIALS1 = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); + public static final String TEST_OUTPUT_ISSUE_88_KDBX = "Issue88.kdbx"; @Test - public void testDefaultAutoType() throws IOException { + public void testDefaultAutoType(@TempDir Path tempPath) throws IOException { KdbxDatabase database; - try (InputStream inputStream = Issue88Test.class.getClassLoader().getResourceAsStream(TEST_RESOURCE1)) { + try (InputStream inputStream = Issue88Test.class.getResourceAsStream(TEST_RESOURCE1)) { assertNotNull(inputStream); database = KdbxDatabase.load(CREDENTIALS1, inputStream); } - Path path = Paths.get(TEST_OUTPUT_ISSUE_88_KDBX); + Path path = tempPath.resolve(TEST_OUTPUT_ISSUE_88_KDBX); try (OutputStream outputStream = Files.newOutputStream(path)) { database.save(CREDENTIALS1, outputStream); } diff --git a/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/package-info.java b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/package-info.java new file mode 100644 index 00000000..f297dc60 --- /dev/null +++ b/kdbx-database/src/test/java/org/linguafranca/pwdb/kdbx/database/validation/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025. Jo Rabin + * Copyright (c) 2026. Sergio Lissner + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +@NullMarked +package org.linguafranca.pwdb.kdbx.database.validation; + +import org.jspecify.annotations.NullMarked; \ No newline at end of file diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 84a78b01..0a71aa57 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -19,7 +19,7 @@ KeePassJava2.parent - org.linguafranca.pwdb + mh.keepass 3.0.0-SNAPSHOT ../pom.xml @@ -32,7 +32,7 @@ - org.linguafranca.pwdb + mh.keepass database ${project.version} @@ -46,7 +46,7 @@ commons-io - org.linguafranca.pwdb + mh.keepass database ${project.version} test-jar diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java index bbaef40b..c01be012 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxHeaderTest.java @@ -21,6 +21,7 @@ import org.apache.commons.io.input.SwappedDataInputStream; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -47,7 +48,7 @@ public void getHmacStream() throws IOException { assertNotNull(inputStream); KdbxHeader header = KdbxSerializer.readOuterHeader(inputStream, new KdbxHeader()); printStream.println("Version " + header.getVersion()); - KdbxCredentials creds = new KdbxCredentials("123".getBytes()); + KdbxCredentials creds = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); KdbxSerializer.readOuterHeaderVerification(header, creds, new SwappedDataInputStream(inputStream)); HmacBlockInputStream hmacBlockInputStream = new HmacBlockInputStream(header.getHmacKey(creds), inputStream, true); String string = IOUtils.toString(hmacBlockInputStream, StandardCharsets.UTF_8); diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java index eecc2ba8..d7e2e54e 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxKeyFileTest.java @@ -20,6 +20,7 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxKeyFile; @@ -118,7 +119,7 @@ public void testSignedKeyFile() throws Exception { InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("kdbx_hash_test.keyx"); assertNotNull(inputStream); assertNotNull(inputStreamKeyFile); - Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES, inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -134,7 +135,7 @@ public void testSignatureFails() throws Exception { assertNotNull(inputStream); assertNotNull(inputStreamKeyFile); Throwable throwable = assertThrows(RuntimeException.class, () -> { - Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES, inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); }); @@ -149,7 +150,7 @@ public void testKeyFileRandom() throws Exception { InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("random_file"); assertNotNull(inputStream); assertNotNull(inputStreamKeyFile); - Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES, inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -163,7 +164,7 @@ public void testKeyFileHex64() throws Exception { InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile64"); assertNotNull(inputStream); assertNotNull(inputStreamKeyFile); - Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES, inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } @@ -177,7 +178,7 @@ public void testKeyFile32() throws Exception { InputStream inputStreamKeyFile = getClass().getClassLoader().getResourceAsStream("keyfile32"); assertNotNull(inputStream); assertNotNull(inputStreamKeyFile); - Credentials credentials = new KdbxCredentials("123".getBytes(), inputStreamKeyFile); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES, inputStreamKeyFile); InputStream decryptedInputStream = createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); toConsole(decryptedInputStream); } diff --git a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java index 24b6eda7..ed43e01d 100644 --- a/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java +++ b/kdbx-io/src/test/java/org/linguafranca/pwdb/kdbx/KdbxSerializerTest.java @@ -20,6 +20,7 @@ import org.apache.commons.io.IOUtils; import org.junit.jupiter.api.Test; import org.linguafranca.pwdb.Credentials; +import org.linguafranca.pwdb.KdbConsts; import org.linguafranca.pwdb.format.KdbxCredentials; import org.linguafranca.pwdb.format.KdbxHeader; import org.linguafranca.pwdb.format.KdbxSerializer; @@ -45,7 +46,7 @@ public class KdbxSerializerTest { public void testGetPlainTextInputStream() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("Attachment.kdbx"); assertNotNull(inputStream); - Credentials credentials = new KdbxCredentials("123".getBytes()); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); printStream.println(IOUtils.toString(decryptedInputStream, StandardCharsets.UTF_8)); } @@ -57,7 +58,7 @@ public void testGetPlainTextInputStream() throws Exception { public void testGetPlainTextInputStream2() throws Exception { InputStream inputStream = getClass().getClassLoader().getResourceAsStream("V4-ChaCha20-AES.kdbx"); assertNotNull(inputStream); - Credentials credentials = new KdbxCredentials("123".getBytes()); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); InputStream decryptedInputStream = KdbxSerializer.createUnencryptedInputStream(credentials, new KdbxHeader(), inputStream); printStream.println(IOUtils.toString(decryptedInputStream, StandardCharsets.UTF_8)); } @@ -71,7 +72,7 @@ public void testGetPlainTextInputStream2() throws Exception { public void testCypherTextOutputStream() throws Exception { File tempFile = File.createTempFile("test", "test"); OutputStream testStream = new FileOutputStream(tempFile); - Credentials credentials = new KdbxCredentials("123".getBytes()); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); OutputStream outputStream = KdbxSerializer.createEncryptedOutputStream(credentials, new KdbxHeader(), testStream); outputStream.write("Hello World\n".getBytes()); @@ -91,7 +92,7 @@ public void testCypherTextOutputStream() throws Exception { public void testCypherTextOutputStream2() throws Exception { File tempFile = File.createTempFile("test", "test"); OutputStream testStream = new FileOutputStream(tempFile); - Credentials credentials = new KdbxCredentials("123".getBytes()); + Credentials credentials = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); KdbxHeader kdbxHeader = new KdbxHeader(4); OutputStream outputStream = KdbxSerializer.createEncryptedOutputStream(credentials, kdbxHeader, testStream); diff --git a/pom.xml b/pom.xml index e03b851d..8b46f999 100644 --- a/pom.xml +++ b/pom.xml @@ -20,7 +20,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 - org.linguafranca.pwdb + mh.keepass KeePassJava2.parent 3.0.0-SNAPSHOT From 4308866d57abd620c43a8c240ff0b0e5e5c73b47 Mon Sep 17 00:00:00 2001 From: sergmain Date: Fri, 8 May 2026 17:30:27 -0700 Subject: [PATCH 66/66] first release of 1.0.0.1 --- all/pom.xml | 2 +- basic/pom.xml | 4 +- database/pom.xml | 2 +- example/pom.xml | 2 +- .../linguafranca/pwdb/kdbx/OpenDbExample.java | 2 +- .../pwdb/kdbx/SimpleQuickStartTest.java | 2 +- kdb/pom.xml | 2 +- kdbx-database/pom.xml | 61 ++++++++++--------- .../jackson/KdbxSerializableDatabase.java | 3 +- kdbx-io/pom.xml | 2 +- pom.xml | 2 +- 11 files changed, 43 insertions(+), 41 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 42ecd0ea..099920a2 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ KeePassJava2.parent mh.keepass - 3.0.0-SNAPSHOT + 1.0.0.1 ../pom.xml diff --git a/basic/pom.xml b/basic/pom.xml index 1d0868c8..87186e99 100644 --- a/basic/pom.xml +++ b/basic/pom.xml @@ -23,7 +23,7 @@ mh.keepass KeePassJava2.parent - 3.0.0-SNAPSHOT + 1.0.0.1 basic @@ -43,7 +43,7 @@ mh.keepass KeePassJava2.kdbx.io - 3.0.0-SNAPSHOT + 1.0.0.1 compile diff --git a/database/pom.xml b/database/pom.xml index 5b5e46ad..5fb14aab 100644 --- a/database/pom.xml +++ b/database/pom.xml @@ -21,7 +21,7 @@ KeePassJava2.parent mh.keepass - 3.0.0-SNAPSHOT + 1.0.0.1 ../pom.xml 4.0.0 diff --git a/example/pom.xml b/example/pom.xml index 86f155c9..2bbba5a6 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -20,7 +20,7 @@ KeePassJava2.parent mh.keepass - 3.0.0-SNAPSHOT + 1.0.0.1 ../pom.xml 4.0.0 diff --git a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java index e3bb0fc4..d268c6bb 100644 --- a/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java +++ b/example/src/main/java/org/linguafranca/pwdb/kdbx/OpenDbExample.java @@ -55,7 +55,7 @@ private static void testDb (DbLoader loader, String label, int loads, int iterat KdbxCredentials creds = new KdbxCredentials(KdbConsts.PASSWORD_BYTES); long start = System.currentTimeMillis(); for (int i=0; i < loads; i++) { - try (InputStream inputStream = OpenDbExample.class.getClassLoader().getResourceAsStream("test1.kdbx")) { + try (InputStream inputStream = OpenDbExample.class.getResourceAsStream("/test1.kdbx")) { Database database = loader.load(creds, inputStream); for (int j = 0; j < iterations; j++) { database.visit(new Visitor.Default() { diff --git a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java index daadc00a..3e730e92 100644 --- a/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java +++ b/example/src/test/java/org/linguafranca/pwdb/kdbx/SimpleQuickStartTest.java @@ -31,7 +31,6 @@ import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.StandardCopyOption; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.parallel.ExecutionMode.CONCURRENT; @@ -73,6 +72,7 @@ public void loadKdbSaveKdbx(@TempDir Path tempPath) throws IOException { @Test public void loadSave(@TempDir Path tempPath) throws IOException { Path actualTempPath = Files.createTempDirectory("kbdx-"); + System.out.println("actualTempPath: " + actualTempPath.toAbsolutePath()); Path path = actualTempPath.resolve("CHACHA-AES-CHACHA.kdbx"); QuickStart.loadKdbx3SaveKdbx4("/test123.kdbx", KdbConsts.PASSWORD_BYTES, path); diff --git a/kdb/pom.xml b/kdb/pom.xml index cab07560..25668d44 100644 --- a/kdb/pom.xml +++ b/kdb/pom.xml @@ -20,7 +20,7 @@ KeePassJava2.parent mh.keepass - 3.0.0-SNAPSHOT + 1.0.0.1 ../pom.xml 4.0.0 diff --git a/kdbx-database/pom.xml b/kdbx-database/pom.xml index 13ce7b54..62d20644 100644 --- a/kdbx-database/pom.xml +++ b/kdbx-database/pom.xml @@ -15,36 +15,37 @@ ~ limitations under the License. ~ --> - - - KeePassJava2.parent - mh.keepass - 3.0.0-SNAPSHOT - ../pom.xml - - 4.0.0 + + + KeePassJava2.parent + mh.keepass + 1.0.0.1 + ../pom.xml + + 4.0.0 - KeePassJava2.kdbx.database - KeePassJava2 :: KDBX-Database - KeePass KDBX file support + KeePassJava2.kdbx.database + KeePassJava2 :: KDBX-Database + KeePass KDBX file support - - - mh.keepass - KeePassJava2.kdbx.io - ${project.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-xml - 2.21.2 - - - mh.keepass - database - ${project.version} - test-jar - test - + + + mh.keepass + KeePassJava2.kdbx.io + ${project.version} + + + com.fasterxml.jackson.dataformat + jackson-dataformat-xml + 2.21.2 + + + mh.keepass + database + ${project.version} + test-jar + test + - + diff --git a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java index dedd9106..fad9e14d 100644 --- a/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java +++ b/kdbx-database/src/main/java/org/linguafranca/pwdb/kdbx/jackson/KdbxSerializableDatabase.java @@ -39,6 +39,7 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; import java.util.Objects; public class KdbxSerializableDatabase implements SerializableDatabase { @@ -113,7 +114,7 @@ public void save(OutputStream outputStream) { xmlOutputFactory.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false); xmlOutputFactory.setProperty(WstxInputProperties.P_RETURN_NULL_FOR_DEFAULT_NAMESPACE, true); - OutputStreamWriter osw = new OutputStreamWriter(outputStream); + OutputStreamWriter osw = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8); XMLStreamWriter sw = xmlOutputFactory.createXMLStreamWriter(osw); //noinspection TryFinallyCanBeTryWithResources - doesn't work with XMLStreamWriter try { diff --git a/kdbx-io/pom.xml b/kdbx-io/pom.xml index 0a71aa57..f1027a84 100644 --- a/kdbx-io/pom.xml +++ b/kdbx-io/pom.xml @@ -20,7 +20,7 @@ KeePassJava2.parent mh.keepass - 3.0.0-SNAPSHOT + 1.0.0.1 ../pom.xml 4.0.0 diff --git a/pom.xml b/pom.xml index 8b46f999..6f1c4b76 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ mh.keepass KeePassJava2.parent - 3.0.0-SNAPSHOT + 1.0.0.1 database kdb