Skip to content

Commit 3443b9d

Browse files
committed
wip
1 parent 13c13ee commit 3443b9d

File tree

10 files changed

+48
-13
lines changed

10 files changed

+48
-13
lines changed

lib/extension_methods.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,10 @@ extension KdbxFileKDF on KdbxFile {
7070
}
7171
return false;
7272
}
73+
74+
List<String> get trimmedTags {
75+
return tags.map((tag) => tag.trim()).where((tag) => tag.isNotEmpty).toSet().toList(growable: false);
76+
}
7377
}
7478

7579
extension KdbxEntryColor on KdbxEntry {

lib/vault_backend/jwt.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,13 +90,13 @@ class JWT {
9090
try {
9191
final keyPair = jwt.JWTKey.fromJWK(jwk);
9292
// If this does not throw an exception then we know the signature is valid
93-
final _ = jwt.JWT.verify(sig, keyPair);
93+
final _ = jwt.JWT.verify(sig, keyPair, checkHeaderType: false);
9494
return ClientVerificationResult(claim: claim, audience: claim.aud);
9595
} on jwt.JWTExpiredException {
9696
l.w('JWT expired. Token should therefore be ignored.');
9797
return ClientVerificationResult(claim: claim, audience: claim.aud);
9898
} on jwt.JWTException catch (ex) {
99-
l.e('Failed to verify JWT', error: ex.message); // e.g. invalid signature
99+
l.e('Failed to verify JWT - "$sig"', error: ex.message); // e.g. invalid signature
100100
throw KeeInvalidJWTException();
101101
} catch (e, stacktrace) {
102102
l.e('General cryptography error during JWT verification', error: e, stackTrace: stacktrace);

lib/widgets/autofill_save.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ class _AutofillSaveWidgetState extends State<AutofillSaveWidget> with TraceableC
114114
return EntryWidget(
115115
key: ValueKey('autofillSaveDetails'),
116116
savingViaAutofill: true,
117-
endEditing: (bool keepChanges) => onEndEditing(keepChanges, vaultCubit, vault.files.current.tags),
117+
endEditing: (bool keepChanges) => onEndEditing(keepChanges, vaultCubit, vault.files.current.trimmedTags),
118118
allCustomIcons: vault.files.current.body.meta.customIcons.map(
119119
(key, value) =>
120120
MapEntry(value, Image.memory(value.data, fit: BoxFit.contain, filterQuality: FilterQuality.low)),

lib/widgets/change_subscription.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ class _ChangeSubscriptionWidgetState extends State<ChangeSubscriptionWidget> {
135135
alignment: Alignment.center,
136136
child: OutlinedButton(
137137
onPressed: () async => await DialogUtils.openUrl('https://kee.pm/keevault/delete-account/'),
138-
style: OutlinedButton.styleFrom(backgroundColor: theme.buttonTheme.colorScheme!.error),
138+
style: OutlinedButton.styleFrom(foregroundColor: theme.buttonTheme.colorScheme!.error),
139139
child: Text(str.deleteAccount),
140140
),
141141
),

lib/widgets/entry.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import 'package:flutter_speed_dial/flutter_speed_dial.dart';
1515

1616
import 'package:keevault/cubit/entry_cubit.dart';
1717
import 'package:keevault/cubit/vault_cubit.dart';
18+
import 'package:keevault/extension_methods.dart';
1819
import 'package:keevault/logging/logger.dart';
1920
import 'package:keevault/model/entry.dart';
2021
import 'package:keevault/model/field.dart';
@@ -495,7 +496,7 @@ class EntryWidget extends StatelessWidget {
495496
),
496497
LabelsWidget(
497498
tags: entry.tags,
498-
otherKnownTags: loadedState.vault.files.current.tags
499+
otherKnownTags: loadedState.vault.files.current.trimmedTags
499500
.map((t) => Tag(t, true))
500501
.where((t) => !entry.tags.any((et) => et.lowercase == t.lowercase))
501502
.toList(),

lib/widgets/entry_list.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ class EntryListItemWidget extends StatelessWidget {
242242
final entryCubit = BlocProvider.of<EntryCubit>(context);
243243
entryCubit.revertToHistoryEntry(entry, index);
244244
final filterCubit = BlocProvider.of<FilterCubit>(context);
245-
filterCubit.reFilter(entry.file!.tags, entry.file!.body.rootGroup);
245+
filterCubit.reFilter(entry.file!.trimmedTags, entry.file!.body.rootGroup);
246246
close(returnValue: false);
247247
//TODO:f: Maybe one day we can automatically reopen the container with the updated information?
248248
// BlocProvider.of<EntryCubit>(context).startEditing(entry);
@@ -252,7 +252,7 @@ class EntryListItemWidget extends StatelessWidget {
252252
final entryCubit = BlocProvider.of<EntryCubit>(context);
253253
entryCubit.removeHistoryEntry(entry, index);
254254
final filterCubit = BlocProvider.of<FilterCubit>(context);
255-
filterCubit.reFilter(entry.file!.tags, entry.file!.body.rootGroup);
255+
filterCubit.reFilter(entry.file!.trimmedTags, entry.file!.body.rootGroup);
256256
close(returnValue: false);
257257
},
258258
);
@@ -271,7 +271,7 @@ class EntryListItemWidget extends StatelessWidget {
271271
final filterCubit = BlocProvider.of<FilterCubit>(context);
272272
await BlocProvider.of<InteractionCubit>(context).entrySaved();
273273
await iam.showIfAppropriate(InAppMessageTrigger.entryChanged);
274-
filterCubit.reFilter(entry.file!.tags, entry.file!.body.rootGroup);
274+
filterCubit.reFilter(entry.file!.trimmedTags, entry.file!.body.rootGroup);
275275
//TODO:f: A separate cubit to track state of ELIVMs might provide better performance and scroll position stability than recreating them all from scratch every time we re-filter?
276276
} else {
277277
entryCubit.endEditing(null);

lib/widgets/label_filter.dart

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
33
import 'package:keevault/cubit/filter_cubit.dart';
44
import 'package:keevault/cubit/vault_cubit.dart';
5+
import 'package:keevault/extension_methods.dart';
56
import 'package:keevault/generated/l10n.dart';
6-
import 'package:keevault/logging/logger.dart';
77

88
class LabelFilterWidget extends StatelessWidget {
99
const LabelFilterWidget({super.key});
@@ -21,7 +21,7 @@ class LabelFilterWidget extends StatelessWidget {
2121
if (state is! FilterActive) return Container();
2222
final filterState = state;
2323
return Visibility(
24-
visible: vaultState.vault.files.current.tags.isNotEmpty,
24+
visible: vaultState.vault.files.current.trimmedTags.isNotEmpty,
2525
replacement: Column(
2626
mainAxisSize: MainAxisSize.min,
2727
mainAxisAlignment: MainAxisAlignment.start,
@@ -51,8 +51,7 @@ class LabelFilterWidget extends StatelessWidget {
5151
crossAxisAlignment: WrapCrossAlignment.center,
5252
alignment: WrapAlignment.center,
5353
runSpacing: 8.0,
54-
children: vaultState.vault.files.current.tags.map((tag) {
55-
l.d('Found tag: "$tag"');
54+
children: vaultState.vault.files.current.trimmedTags.map((tag) {
5655
final isSelected = filterState.tags.contains(tag.toLowerCase());
5756
return FilterChip(
5857
visualDensity: VisualDensity.standard,

lib/widgets/new_entry_button.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:kdbx/kdbx.dart';
55
import 'package:keevault/cubit/autofill_cubit.dart';
66
import 'package:keevault/cubit/entry_cubit.dart';
77
import 'package:keevault/cubit/filter_cubit.dart';
8+
import 'package:keevault/extension_methods.dart';
89
import 'package:keevault/widgets/in_app_messenger.dart';
910
import '../cubit/account_cubit.dart';
1011
import '../cubit/interaction_cubit.dart';
@@ -58,7 +59,7 @@ class NewEntryButton extends StatelessWidget {
5859
final filterCubit = BlocProvider.of<FilterCubit>(context);
5960
await BlocProvider.of<InteractionCubit>(context).entrySaved();
6061
await iam.showIfAppropriate(InAppMessageTrigger.entryChanged);
61-
filterCubit.reFilter(currentFile.tags, currentFile.body.rootGroup);
62+
filterCubit.reFilter(currentFile.trimmedTags, currentFile.body.rootGroup);
6263
} else {
6364
entryCubit.endCreating(null);
6465
final vaultCubit = BlocProvider.of<VaultCubit>(context);

lib/widgets/settings.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@ class _BiometricSettingWidgetState extends State<BiometricSettingWidget> {
365365
@override
366366
Widget build(BuildContext context) {
367367
final str = S.of(context);
368+
final theme = Theme.of(context);
368369
final quickUnlockSettings = [
369370
TextInputSettingsTile(
370371
title: str.automaticallySignInFor,
@@ -427,6 +428,7 @@ class _BiometricSettingWidgetState extends State<BiometricSettingWidget> {
427428
visible: !KeeVaultPlatform.isIOS,
428429
replacement: Column(children: quickUnlockSettings),
429430
child: SwitchSettingsTile(
431+
activeColor: theme.colorScheme.inversePrimary,
430432
settingKey: 'biometrics-enabled',
431433
title: str.biometricSignIn,
432434
onChange: (value) async {
@@ -509,6 +511,7 @@ class _AutofillStatusWidgetState extends State<AutofillStatusWidget> {
509511
@override
510512
Widget build(BuildContext context) {
511513
final str = S.of(context);
514+
final theme = Theme.of(context);
512515

513516
return Column(
514517
children: [
@@ -552,6 +555,7 @@ class _AutofillStatusWidgetState extends State<AutofillStatusWidget> {
552555
Visibility(
553556
visible: widget.isEnabled && KeeVaultPlatform.isAndroid,
554557
child: SwitchSettingsTile(
558+
activeColor: theme.colorScheme.inversePrimary,
555559
settingKey: 'autofillServiceEnableSaving',
556560
title: str.offerToSave,
557561
defaultValue: true,
@@ -569,6 +573,7 @@ class _AutofillStatusWidgetState extends State<AutofillStatusWidget> {
569573
visible:
570574
widget.isEnabled && KeeVaultPlatform.isAndroid && (_androidDeviceInfo?.version.sdkInt ?? 0) >= 31,
571575
child: SwitchSettingsTile(
576+
activeColor: theme.colorScheme.inversePrimary,
572577
settingKey: 'autofillServiceEnableIMEIntegration',
573578
title: 'Show in keyboard',
574579
subtitle: 'experimental feature',

test/jwt_test.dart

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// dart
2+
import 'package:flutter_test/flutter_test.dart';
3+
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
4+
5+
void main() {
6+
group('JWT verification', () {
7+
test('verifies a valid JWT signed with idBeta JWK', () async {
8+
const sig =
9+
'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJSTmk4RTI2N1pCaHk3WlBjUFJpOXhsSWFzTTlKdDRrb2t4TExVZGNMTkpJPSIsImlzcyI6ImlkQmV0YSIsImF1ZCI6ImNsaWVudCIsImV4cCI6MTc1MTAyOTIyNDM2MCwiaWF0IjoxNzUwOTQyODI0MzYwLCJmZWF0dXJlcyI6WyJzdG9yYWdlLWtlZSIsIm11bHRpU2Vzc2lvbiIsInN5bmNTZXR0aW5ncyIsImZvcm1BY2N1cmFjeSJdLCJmZWF0dXJlRXhwaXJ5IjoxNzY0NjA3MTQzMDAwLCJzdWJzY3JpcHRpb25JZCI6ImNiXzFta1Z2dERSQVdmQVpaRkI3In0.Jf_XXlAFE8L1Evz03nCNA7vevMOFrB9ZKJ6eRIdlurzwDjOaiLl8WIjgfClSGKLw-HlKMmEuP_Kr7ExPlKEm_w';
10+
// const sig =
11+
// 'eyJhbGciOiJFUzI1NiJ9.eyJzdWIiOiJSTmk4RTI2N1pCaHk3WlBjUFJpOXhsSWFzTTlKdDRrb2t4TExVZGNMTkpJPSIsImlzcyI6ImlkQmV0YSIsImF1ZCI6ImNsaWVudCIsImV4cCI6MTc1MTAyOTIyNDM2MCwiaWF0IjoxNzUwOTQyODI0MzYwLCJmZWF0dXJlcyI6WyJzdG9yYWdlLWtlZSIsIm11bHRpU2Vzc2lvbiIsInN5bmNTZXR0aW5ncyIsImZvcm1BY2N1cmFjeSJdLCJmZWF0dXJlRXhwaXJ5IjoxNzY0NjA3MTQzMDAwLCJzdWJzY3JpcHRpb25JZCI6ImNiXzFta1Z2dERSQVdmQVpaRkI3In0.Jf/XXlAFE8L1Evz03nCNA7vevMOFrB9ZKJ6eRIdlurzwDjOaiLl8WIjgfClSGKLw+HlKMmEuP/Kr7ExPlKEm/w';
12+
13+
final keyPair = JWTKey.fromJWK({
14+
'kty': 'EC',
15+
'crv': 'P-256',
16+
'x': 'CinRkFHv6IGNcd52YlzD3BF_WruIMs-6Nn5oI7QmgjU',
17+
'y': 'pJ66MRPoCC2MUBFdYyRqGPfw3pZEnPGtHVhvspLTVDA',
18+
});
19+
final result = JWT.verify(sig, keyPair, checkHeaderType: false);
20+
// expect(result, isA<ClientVerificationResult>());
21+
final clientResult = result;
22+
expect(clientResult.audience, 'client');
23+
});
24+
});
25+
}

0 commit comments

Comments
 (0)