Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
luckyrat committed Feb 8, 2024
1 parent b9f7e7d commit baebf8a
Show file tree
Hide file tree
Showing 10 changed files with 150 additions and 14 deletions.
1 change: 1 addition & 0 deletions lib/src/kdbx_custom_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:kdbx/src/kdbx_object.dart';
import 'package:kdbx/src/kdbx_xml.dart';
import 'package:xml/xml.dart' as xml;

//TODO: create KdbxFileCustomData with date too
class KdbxCustomData extends KdbxNode {
KdbxCustomData.create()
: _data = {},
Expand Down
3 changes: 2 additions & 1 deletion lib/src/kdbx_entry.dart
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,8 @@ class KdbxEntry extends KdbxObject {
ColorNode get foregroundColor => ColorNode(this, 'ForegroundColor');
ColorNode get backgroundColor => ColorNode(this, 'BackgroundColor');
StringNode get overrideURL => StringNode(this, 'OverrideURL');
StringListNode get tags => StringListNode(this, 'Tags');
NullableBooleanNode get qualityCheck =>
NullableBooleanNode(this, 'QualityCheck');

@override
set file(KdbxFile? file) {
Expand Down
8 changes: 5 additions & 3 deletions lib/src/kdbx_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,14 @@ class KdbxFile {
return recycleBin ?? _createRecycleBin();
}

/// Upgrade v3 file to v4.
void upgrade(int majorVersion) {
/// Upgrade v3 file to v4.x
void upgrade(int majorVersion, int minorVersion) {
checkArgument(majorVersion == 4, message: 'Must be majorVersion 4');
body.meta.settingsChanged.setToNow();
body.meta.headerHash.remove();
header.upgrade(majorVersion);
header.version.major == 4
? header.upgradeMinor(majorVersion, minorVersion)
: header.upgrade(majorVersion, minorVersion);
}

/// Merges the given file into this file.
Expand Down
4 changes: 2 additions & 2 deletions lib/src/kdbx_format.dart
Original file line number Diff line number Diff line change
Expand Up @@ -519,14 +519,14 @@ class KdbxFormat {
static bool dartWebWorkaround = false;

/// Creates a new, empty [KdbxFile] with default settings.
/// If [header] is not given by default a kdbx 4.0 file will be created.
/// If [header] is not given by default a kdbx 4.1 file will be created.
KdbxFile create(
Credentials credentials,
String name, {
String? generator,
KdbxHeader? header,
}) {
header ??= KdbxHeader.createV4();
header ??= KdbxHeader.createV4_1();
final ctx = KdbxReadWriteContext(header: header);
final meta = KdbxMeta.create(
databaseName: name,
Expand Down
1 change: 1 addition & 0 deletions lib/src/kdbx_group.dart
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ class KdbxGroup extends KdbxObject {
enableAutoType,
enableSearching,
lastTopVisibleEntry,
//TODO: More fields for 4.1?
];

void _overwriteFrom(OverwriteContext mergeContext, KdbxGroup other) {
Expand Down
13 changes: 10 additions & 3 deletions lib/src/kdbx_header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -569,11 +569,18 @@ class KdbxHeader {
void writeKdfParameters(VarDictionary kdfParameters) =>
_setHeaderField(HeaderFields.KdfParameters, kdfParameters.write());

void upgrade(int majorVersion) {
void upgradeMinor(int majorVersion, int minorVersion) {
checkArgument(majorVersion == KdbxVersion.V4.major,
message: 'Can only upgrade v4');
_logger.info('Upgrading header to $minorVersion');
_version = KdbxVersion._(majorVersion, minorVersion);
}

void upgrade(int majorVersion, int minorVersion) {
checkArgument(majorVersion == KdbxVersion.V4.major,
message: 'Can only upgrade to 4');
_logger.info('Upgrading header to $majorVersion');
_version = KdbxVersion._(majorVersion, 0);
_logger.info('Upgrading header to $majorVersion.$minorVersion');
_version = KdbxVersion._(majorVersion, minorVersion);
if (fields[HeaderFields.KdfParameters] == null) {
_logger.fine('Creating kdf parameters.');
writeKdfParameters(_createKdfDefaultParameters());
Expand Down
2 changes: 2 additions & 0 deletions lib/src/kdbx_object.dart
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ abstract class KdbxObject extends KdbxNode {
late final UuidNode previousParentGroup =
UuidNode(this, 'PreviousParentGroup');

StringListNode get tags => StringListNode(this, 'Tags');

bool get isInRecycleBin {
final bin = file!.recycleBin;
if (bin == null) {
Expand Down
100 changes: 100 additions & 0 deletions test/kdbx4_1_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
@Tags(['kdbx4_1'])

import 'dart:io';

import 'package:clock/clock.dart';
import 'package:kdbx/kdbx.dart';
import 'package:logging/logging.dart';
import 'package:logging_appenders/logging_appenders.dart';
import 'package:test/test.dart';

import 'internal/test_utils.dart';

final _logger = Logger('kdbx4_1_test');

// ignore_for_file: non_constant_identifier_names

void main() {
Logger.root.level = Level.ALL;
PrintAppender().attachToLogger(Logger.root);
final kdbxFormat = TestUtil.kdbxFormat();
if (!kdbxFormat.argon2.isFfi) {
throw StateError('Expected ffi!');
}
var now = DateTime.fromMillisecondsSinceEpoch(0);

final fakeClock = Clock(() => now);
void proceedSeconds(int seconds) {
now = now.add(Duration(seconds: seconds));
}

setUp(() {
now = DateTime.fromMillisecondsSinceEpoch(0);
});

group('11111111111111', () {
// test('TODO', () async {
// final file = await TestUtil.readKdbxFile('test/kdbx4.kdbx');
// //final file = await TestUtil.readKdbxFile('test/kdbx4_1.kdbx');
// final firstEntry = file.body.rootGroup.entries.first;
// final d = DateTime.utc(2020, 4, 5, 10, 0);
// firstEntry.times.lastModificationTime.set(d);
// final saved = await file.save();
// {
// final file2 = await TestUtil.readKdbxFileBytes(saved);
// final firstEntry = file2.body.rootGroup.entries.first;
// expect(firstEntry.times.lastModificationTime.get(), d);
// }
// });

test('Tags work on entries and groups', () async {
final credentials = Credentials(ProtectedValue.fromString('asdf'));
final kdbx = kdbxFormat.create(
credentials,
'Test Keystore',
header: KdbxHeader.createV4(),
);
final rootGroup = kdbx.body.rootGroup;
final e = TestUtil.createEntry(kdbx, rootGroup, 'user1', 'LoremIpsum');
TestUtil.createEntry(kdbx, rootGroup, 'user2', 'Second Password');
rootGroup.tags.set(['t1', 't2']);
e.tags.set(['t3', 't4']);
final saved = await kdbx.save();

final loadedKdbx = await kdbxFormat.read(
saved, Credentials(ProtectedValue.fromString('asdf')));
//final file2 = await TestUtil.readKdbxFileBytes(saved);

_logger.fine('Successfully loaded kdbx $loadedKdbx');
final firstEntry = loadedKdbx.body.rootGroup.entries.first;
expect(loadedKdbx.body.rootGroup.tags.get(), ['t1', 't2']);
expect(firstEntry.tags.get(), ['t3', 't4']);
});

test('Entry password quality estimation', () async {
final credentials = Credentials(ProtectedValue.fromString('asdf'));
final kdbx = kdbxFormat.create(
credentials,
'Test Keystore',
header: KdbxHeader.createV4(),
);
final rootGroup = kdbx.body.rootGroup;
final e1 = TestUtil.createEntry(kdbx, rootGroup, 'user1', 'LoremIpsum');
final e2 =
TestUtil.createEntry(kdbx, rootGroup, 'user2', 'Second Password');
expect(e1.qualityCheck.get(), null);
e1.qualityCheck.set(true);
e2.qualityCheck.set(false);
final saved = await kdbx.save();

final loadedKdbx = await kdbxFormat.read(
saved, Credentials(ProtectedValue.fromString('asdf')));

_logger.fine('Successfully loaded kdbx $loadedKdbx');
final entry1 = loadedKdbx.body.rootGroup.entries.first;
final entry2 = loadedKdbx.body.rootGroup.entries.last;
expect(entry1?.qualityCheck.get(), true);
expect(entry2?.qualityCheck.get(), false);
});
});
}
30 changes: 25 additions & 5 deletions test/kdbx_upgrade_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,41 @@ import 'internal/test_utils.dart';

void main() {
TestUtil.setupLogging();
group('Test upgrade from v3 to v4', () {
group('Test kdbx format upgrades', () {
final format = TestUtil.kdbxFormat();
test('Read v3, write v4', () async {
final file =
await TestUtil.readKdbxFile('test/FooBar.kdbx', password: 'FooBar');
expect(file.header.version, KdbxVersion.V3_1);
file.upgrade(KdbxVersion.V4.major);
file.upgrade(KdbxVersion.V4.major, 0);
final v4 = await TestUtil.saveAndRead(file);
expect(v4.header.version, KdbxVersion.V4);
await TestUtil.saveTestOutput('kdbx4upgrade', v4);
await TestUtil.saveTestOutput('kdbx4upgrade3-4', v4);
}, tags: 'kdbx3');
test('kdbx4 is the new default', () async {

test('Read v3, write v4.1', () async {
final file =
format.create(Credentials(ProtectedValue.fromString('asdf')), 'test');
await TestUtil.readKdbxFile('test/FooBar.kdbx', password: 'FooBar');
expect(file.header.version, KdbxVersion.V3_1);
file.upgrade(KdbxVersion.V4.major, 1);
final v4 = await TestUtil.saveAndRead(file);
expect(v4.header.version, KdbxVersion.V4_1);
await TestUtil.saveTestOutput('kdbx4upgrade3-41', v4);
}, tags: 'kdbx4');

test('Read v4, write v4.1', () async {
final file = await TestUtil.readKdbxFile('test/kdbx4_keeweb.kdbx',
password: 'asdf');
expect(file.header.version, KdbxVersion.V4);
file.upgrade(KdbxVersion.V4.major, 1);
final v4 = await TestUtil.saveAndRead(file);
expect(v4.header.version, KdbxVersion.V4_1);
await TestUtil.saveTestOutput('kdbx4upgrade4-41', v4);
}, tags: 'kdbx4');
test('kdbx4.1 is the new default', () async {
final file =
format.create(Credentials(ProtectedValue.fromString('asdf')), 'test');
expect(file.header.version, KdbxVersion.V4_1);
});
}, tags: ['kdbx4']);
}
2 changes: 2 additions & 0 deletions test/merge/kdbx_merge_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ void main() {
);
});

//TODO: https://github.com/authpass/authpass/issues/335

group('Real merges', () {
test('Local file custom data wins', () async {
await withClock(fakeClock, () async {
Expand Down

0 comments on commit baebf8a

Please sign in to comment.