Skip to content

Commit

Permalink
Moved ContainerTokenSyncIcon to the issuer
Browse files Browse the repository at this point in the history
  • Loading branch information
frankmer committed Oct 17, 2024
1 parent 3a2a899 commit d5d0db3
Show file tree
Hide file tree
Showing 24 changed files with 349 additions and 264 deletions.
13 changes: 8 additions & 5 deletions lib/api/privacy_idea_container_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import '../model/riverpod_states/token_state.dart';
import '../model/token_container.dart';
import '../model/token_template.dart';
import '../model/tokens/token.dart';
import '../utils/app_info_utils.dart';
import '../utils/globals.dart';
import '../utils/identifiers.dart';
import '../utils/logger.dart';
Expand Down Expand Up @@ -116,14 +117,16 @@ class PrivacyIdeaContainerApi {
'|${container.timestamp.toIso8601String().replaceFirst('Z', '+00:00')}'
'|${container.finalizationUrl}'
'|${container.serial}'
'|${AppInfoUtils.deviceId}'
'${passphrase != null ? '|$passphrase' : ''}';

final signature = eccUtils.signWithPrivateKey(ecPrivateClientKey, message);

final body = {
'container_serial': container.serial,
'public_client_key': container.publicClientKey,
'signature': signature,
CONTAINER_CONTAINER_SERIAL: container.serial,
CONTAINER_PUBLIC_CLIENT_KEY: container.publicClientKey,
CONTAINER_DEVICE_ID: AppInfoUtils.deviceId,
CONTAINER_SIGNATURE: signature,
};
return await _ioClient.doPost(url: container.finalizationUrl, body: body, sslVerify: false); //TODO: sslVerify
}
Expand Down Expand Up @@ -168,8 +171,8 @@ class PrivacyIdeaContainerApi {

final containerDict = {
CONTAINER_DICT_SERIAL: container.serial,
CONTAINER_DICT_TYPE: 'smartphone',
'tokens': otpAuthMaps,
CONTAINER_DICT_TYPE: CONTAINER_DICT_TYPE_SMARTPHONE,
CONTAINER_DICT_TOKENS: otpAuthMaps,
};
final signMessage =
'${challenge.nonce}|${challenge.timeStamp}|${container.serial}|${challenge.finalizeSyncUrl}|$publicKeyBase64|${jsonEncode(containerDict)}';
Expand Down
2 changes: 1 addition & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1298,7 +1298,7 @@
"exportingTokens": "Exporting tokens...",
"failedToFinalizeContainer": "Failed to finalize the container {serial}",
"failedToLoad": "Failed to load: \"{name}\"",
"failedToSyncContainer": "Failes to sync container {serial}",
"failedToSyncContainer": "Failed to sync container {serial}",
"feedback": "Feedback",
"feedbackDescription": "If you have any questions, suggestions or problems, please let us know.",
"feedbackHint": "A ready-made e-mail will open, which you can send to us. If desired, information about your device and the version of the application will be added. You can check and edit the email before sending it.",
Expand Down
5 changes: 5 additions & 0 deletions lib/model/exception_errors/response_error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,9 @@ class ResponseError {
assert(response.statusCode != 200, 'Status code of an response error should not be 200');
return ResponseError._(response.statusCode, response.body);
}

@override
String toString() {
return '$statusCode: $message';
}
}
2 changes: 1 addition & 1 deletion lib/repo/secure_token_repository.dart
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class SecureTokenRepository implements TokenRepository {
child: SizedBox(
height: 50,
width: 50,
child: CircularProgressIndicator(),
child: CircularProgressIndicator.adaptive(),
),
),
);
Expand Down
4 changes: 4 additions & 0 deletions lib/utils/app_info_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class AppInfoUtils {
_appBuildNumber = packageInfo.buildNumber;
_androidInfo = !kIsWeb && Platform.isAndroid ? await _deviceInfo.androidInfo : null;
_iosInfo = !kIsWeb && Platform.isIOS ? await _deviceInfo.iosInfo : null;
_deviceId = !kIsWeb ? (Platform.isAndroid ? _androidInfo!.id : _iosInfo!.identifierForVendor ?? 'N/A') : 'Web: Not available.';

isInitialized = true;
}
Expand All @@ -58,6 +59,9 @@ class AppInfoUtils {
static String get currentBuildNumber => isInitialized ? _appBuildNumber : throw Exception('AppInfoUtils not initialized');
static late final String _appBuildNumber;

static String get deviceId => isInitialized ? _deviceId : throw Exception('AppInfoUtils not initialized');
static late final String _deviceId;

static String get dartVersion => Platform.version;
static String get platform => Platform.operatingSystem;

Expand Down
41 changes: 23 additions & 18 deletions lib/utils/firebase_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,10 @@ class FirebaseUtils {
);
String errorMessage = e.message ?? 'no error message';
final SnackBar snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
"Firebase notification permission error! ($errorMessage: ${e.code}",
));
"Firebase notification permission error! ($errorMessage: ${e.code}",
));
globalSnackbarKey.currentState?.showSnackBar(snackBar);
}

Expand All @@ -87,28 +88,31 @@ class FirebaseUtils {
} else {
String errorMessage = error.message ?? 'no error message';
final SnackBar snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
'Push cant be initialized, restart the app and try again. ${error.code}: $errorMessage',
overflow: TextOverflow.fade,
softWrap: false,
));
'Push cant be initialized, restart the app and try again. ${error.code}: $errorMessage',
overflow: TextOverflow.fade,
softWrap: false,
));
globalSnackbarKey.currentState?.showSnackBar(snackBar);
}
} on FirebaseException catch (error) {
final SnackBar snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
"Push cant be initialized, restart the app and try again$error",
overflow: TextOverflow.fade,
softWrap: false,
));
"Push cant be initialized, restart the app and try again$error",
overflow: TextOverflow.fade,
softWrap: false,
));
globalSnackbarKey.currentState?.showSnackBar(snackBar);
} catch (error) {
final SnackBar snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
"Unknown error: $error",
overflow: TextOverflow.fade,
softWrap: false,
));
"Unknown error: $error",
overflow: TextOverflow.fade,
softWrap: false,
));
globalSnackbarKey.currentState?.showSnackBar(snackBar);
}

Expand All @@ -120,11 +124,12 @@ class FirebaseUtils {
updateFirebaseToken(newToken);
} catch (error) {
final SnackBar snackBar = SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
"Unknown error: $error",
overflow: TextOverflow.fade,
softWrap: false,
));
"Unknown error: $error",
overflow: TextOverflow.fade,
softWrap: false,
));
globalSnackbarKey.currentState?.showSnackBar(snackBar);
}
}
Expand Down
7 changes: 7 additions & 0 deletions lib/utils/identifiers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,11 @@ const String CONTAINER_EC_KEY_ALGORITHM = 'key_algorithm';
const String CONTAINER_HASH_ALGORITHM = 'hash_algorithm';
const String CONTAINER_PASSPHRASE_QUESTION = 'passphrase';

const String CONTAINER_CONTAINER_SERIAL = 'container_serial';
const String CONTAINER_PUBLIC_CLIENT_KEY = 'public_client_key';
const String CONTAINER_DEVICE_ID = 'device_id';
const String CONTAINER_SIGNATURE = 'signature';

// Container sync:
const String CONTAINER_SYNC_NONCE = 'nonce';
const String CONTAINER_SYNC_TIMESTAMP = 'time_stamp';
Expand All @@ -132,7 +137,9 @@ const String CONTAINER_SYNC_DICT_CLIENT = 'container_dict_client';

const String CONTAINER_DICT_SERIAL = 'serial';
const String CONTAINER_DICT_TYPE = 'type';
const String CONTAINER_DICT_TYPE_SMARTPHONE = 'smartphone';
const String CONTAINER_DICT_TOKENS = 'tokens';

const String CONTAINER_DICT_TOKENS_ADD = 'add';
const String CONTAINER_DICT_TOKENS_UPDATE = 'update';

Expand Down
2 changes: 2 additions & 0 deletions lib/utils/logger.dart
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ Device Parameters $deviceInfo""";
await file.writeAsString('', mode: FileMode.write);
globalSnackbarKey.currentState?.showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
_context != null ? AppLocalizations.of(_context!)!.errorLogCleared : 'Error Log Cleared',
overflow: TextOverflow.fade,
Expand Down Expand Up @@ -369,6 +370,7 @@ Device Parameters $deviceInfo""";
WidgetsBinding.instance.addPostFrameCallback((_) {
globalSnackbarKey.currentState?.showSnackBar(
SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(
_context != null ? AppLocalizations.of(_context!)!.unexpectedError : 'An unexpected error occurred.',
),
Expand Down
6 changes: 5 additions & 1 deletion lib/utils/view_utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,11 @@ void showMessage({
return;
}
globalSnackbarKey.currentState!.showSnackBar(
SnackBar(content: Text(message), duration: duration),
SnackBar(
behavior: SnackBarBehavior.floating,
content: Text(message),
duration: duration,
),
);
}

Expand Down
46 changes: 2 additions & 44 deletions lib/views/container_view/container_widgets/container_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,13 @@
*/
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:privacyidea_authenticator/l10n/app_localizations.dart';
import 'package:privacyidea_authenticator/model/extensions/enums/rollout_state_extension.dart';
import 'package:privacyidea_authenticator/widgets/button_widgets/cooldown_button.dart';

import '../../../model/enums/sync_state.dart';
import '../../../model/token_container.dart';
import '../../../utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart';
import '../../../utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart';
import '../../../widgets/pi_slidable.dart';
import '../../main_view/main_view_widgets/token_widgets/token_widget_tile.dart';
import '../container_view.dart';
import 'container_actions/delete_container_action.dart';
import 'container_actions/details_container_action.dart';
import 'container_widget_tile.dart';

class ContainerWidget extends ConsumerWidget {
final TokenContainer container;
Expand All @@ -56,43 +50,7 @@ class ContainerWidget extends ConsumerWidget {
DetailsContainerAction(container: container, key: Key('${container.serial}-EditContainerAction')),
],
stack: stack,
child: TokenWidgetTile(
title: Text(
container.serial,
style: Theme.of(context).textTheme.titleMedium,
),
subtitles: [
AppLocalizations.of(context)!.issuerLabel(container.issuer),
'${container.finalizationState.rolloutMsgLocalized(AppLocalizations.of(context)!)}',
],
trailing: _getTrailing(context, ref),
),
child: ContainerWidgetTile(container: container),
),
);

Widget _getTrailing(BuildContext context, WidgetRef ref) {
if (container is TokenContainerFinalized) {
return CooldownButton(
styleType: CooldownButtonStyleType.iconButton,
childWhenCooldown: CircularProgressIndicator(),
isPressable: (container as TokenContainerFinalized).syncState != SyncState.syncing,
onPressed: () async {
final tokenState = ref.read(tokenProvider);
await ref.read(tokenContainerProvider.notifier).syncTokens(tokenState, containersToSync: [container as TokenContainerFinalized], isManually: true);
},
child: (container as TokenContainerFinalized).syncState == SyncState.failed ? const Icon(Icons.sync_problem) : const Icon(Icons.sync),
);
}
if (container.finalizationState.isFailed) {
return CooldownButton(
styleType: CooldownButtonStyleType.iconButton,
childWhenCooldown: CircularProgressIndicator(),
onPressed: () async {
await ref.read(tokenContainerProvider.notifier).finalize(container, isManually: true);
},
child: const Icon(Icons.link_rounded),
);
}
return CircularProgressIndicator();
}
}
111 changes: 111 additions & 0 deletions lib/views/container_view/container_widgets/container_widget_tile.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
* privacyIDEA Authenticator
*
* Author: Frank Merkel <[email protected]>
*
* Copyright (c) 2024 NetKnights GmbH
*
* 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 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:privacyidea_authenticator/model/extensions/enums/rollout_state_extension.dart';

import '../../../l10n/app_localizations.dart';
import '../../../model/enums/sync_state.dart';
import '../../../model/token_container.dart';
import '../../../utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart';
import '../../../utils/riverpod/riverpod_providers/generated_providers/token_notifier.dart';
import '../../../widgets/button_widgets/cooldown_button.dart';

class ContainerWidgetTile extends ConsumerWidget {
final TokenContainer container;

const ContainerWidgetTile({required this.container, super.key});

@override
Widget build(BuildContext context, WidgetRef ref) => ListTile(
contentPadding: const EdgeInsets.symmetric(horizontal: 2),
titleAlignment: ListTileTitleAlignment.center,
title: FittedBox(
fit: BoxFit.scaleDown,
alignment: Alignment.topLeft,
child: Align(
alignment: Alignment.centerLeft,
child: Tooltip(
message: AppLocalizations.of(context)!.containerSerial,
triggerMode: TooltipTriggerMode.longPress,
child: Text(
container.serial,
style: Theme.of(context).textTheme.titleMedium,
),
),
),
),
subtitle: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: Padding(
padding: const EdgeInsets.only(left: 4.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
for (var line in [
AppLocalizations.of(context)!.issuerLabel(container.issuer),
'${container.finalizationState.rolloutMsgLocalized(AppLocalizations.of(context)!)}',
])
Text(
line,
style: Theme.of(context).listTileTheme.subtitleTextStyle,
textAlign: TextAlign.left,
overflow: TextOverflow.fade,
softWrap: false,
),
],
),
),
),
],
),
trailing: _getTrailing(context, ref),
);

Widget _getTrailing(BuildContext context, WidgetRef ref) {
if (container is TokenContainerFinalized) {
return CooldownButton(
styleType: CooldownButtonStyleType.iconButton,
childWhenCooldown: CircularProgressIndicator.adaptive(),
isPressable: (container as TokenContainerFinalized).syncState != SyncState.syncing,
onPressed: () async {
final tokenState = ref.read(tokenProvider);
await ref.read(tokenContainerProvider.notifier).syncTokens(tokenState, containersToSync: [container as TokenContainerFinalized], isManually: true);
},
child: (container as TokenContainerFinalized).syncState == SyncState.failed ? const Icon(Icons.sync_problem) : const Icon(Icons.sync),
);
}
if (container.finalizationState.isFailed) {
return CooldownButton(
styleType: CooldownButtonStyleType.iconButton,
childWhenCooldown: CircularProgressIndicator.adaptive(),
onPressed: () async {
await ref.read(tokenContainerProvider.notifier).finalize(container, isManually: true);
},
child: const Icon(Icons.link_rounded),
);
}
return CircularProgressIndicator.adaptive();
}
}
2 changes: 1 addition & 1 deletion lib/views/import_tokens_view/pages/import_start_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ class _ImportStartPageState extends ConsumerState<ImportStartPage> {
},
),
)
: const CircularProgressIndicator(),
: const CircularProgressIndicator.adaptive(),
],
),
),
Expand Down
Loading

0 comments on commit d5d0db3

Please sign in to comment.