Skip to content

Commit

Permalink
add container from link
Browse files Browse the repository at this point in the history
  • Loading branch information
frankmer committed Feb 10, 2025
1 parent bbd0ab7 commit 0cf485b
Show file tree
Hide file tree
Showing 12 changed files with 63 additions and 16 deletions.
1 change: 1 addition & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
<!-- Accepts URIs that begin with YOUR_SCHEME://-->
<data android:scheme="otpauth" />
<data android:scheme="otpauth-migration" />
<data android:scheme="pia" />
</intent-filter>
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
Expand Down
2 changes: 2 additions & 0 deletions ios/Runner/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
<key>CFBundleURLSchemes</key>
<array>
<string>otpauth</string>
<string>otpauth-migration</string>
<string>pia</string>
</array>
</dict>
</array>
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 @@ -1920,7 +1920,7 @@
"selectFile": "Select file",
"selectImportSource": "Select import source",
"selectImportType": "How do you want to import the tokens?",
"selectTokensToExport": "{count, plural, one{select the token to be exported} other{select the tokens to be exported}}",
"selectTokensToExport": "{count, plural, one{Select the token to be exported} other{Select the tokens to be exported}}",
"selectTokensToExportHelpContent1": "If a token is not listed, it is not guaranteed that it is not a privacyIDEA token.",
"selectTokensToExportHelpContent2": "Currently only manually added and imported tokens are exportable.",
"selectTokensToExportHelpContent3": "We are working on a solution to differentiate between privacyIDEA tokens and private tokens.",
Expand Down
4 changes: 2 additions & 2 deletions lib/model/processor_result.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,6 @@ extension ListProcessorResult<T> on List<ProcessorResult<T>> {

mixin ResultHandler {
static const argTokenOriginSourceType = "TokenOriginSourceType";
Future handleProcessorResult(ProcessorResult result, Map<String, dynamic> args);
Future handleProcessorResults(List<ProcessorResult> results, Map<String, dynamic> args);
Future handleProcessorResult(ProcessorResult result, {Map<String, dynamic> args = const {}});
Future handleProcessorResults(List<ProcessorResult> results, {Map<String, dynamic> args = const {}});
}
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ class HomeWidgetNavigateProcessor implements NavigationSchemeProcessor {

class NavigationHandler<R> with ResultHandler {
@override
Future<R?> handleProcessorResult(ProcessorResult result, Map<String, dynamic> args) async {
Future<R?> handleProcessorResult(ProcessorResult result, {Map<String, dynamic> args = const {}}) async {
if (result is! ProcessorResult<Navigation>) return null;
if (result.isFailed) return null;
validate(
Expand All @@ -162,7 +162,7 @@ class NavigationHandler<R> with ResultHandler {
}

@override
Future<List<R>?> handleProcessorResults(List<ProcessorResult> results, Map<String, dynamic> args) async {
Future<List<R>?> handleProcessorResults(List<ProcessorResult> results, {Map<String, dynamic> args = const {}}) async {
final successResults = results.whereType<ProcessorResult<Navigation>>().toList().successResults;
if (successResults.isEmpty) return null;
final BuildContext context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler
if (uri == null) throw ArgumentError('Invalid rollover uri');
final result = (await TokenContainerProcessor().processUri(uri, fromInit: false))?.firstOrNull;
if (result == null) throw StateError('Failed to process rollover uri');
final success = await handleProcessorResult(result, {TokenContainerProcessor.ARG_DO_REPLACE: true});
final success = await handleProcessorResult(result, args: {TokenContainerProcessor.ARG_DO_REPLACE: true});
return success;
}

Expand Down Expand Up @@ -416,14 +416,14 @@ class TokenContainerNotifier extends _$TokenContainerNotifier with ResultHandler

/// Returns true if the processor result was handled successfully
@override
Future<bool> handleProcessorResult(ProcessorResult result, Map<String, dynamic> args) async {
final failedContainer = await handleProcessorResults([result], args);
Future<bool> handleProcessorResult(ProcessorResult result, {Map<String, dynamic> args = const {}}) async {
final failedContainer = await handleProcessorResults([result], args: args);
return failedContainer?.isEmpty ?? false;
}

/// Returns a list of containers that failed to add
@override
Future<List<TokenContainerUnfinalized>?> handleProcessorResults(List<ProcessorResult> results, Map<String, dynamic> args) async {
Future<List<TokenContainerUnfinalized>?> handleProcessorResults(List<ProcessorResult> results, {Map<String, dynamic> args = const {}}) async {
Logger.info('Handling processor results');
final newContainers = results.getData().whereType<TokenContainerUnfinalized>().toList();
final validatedArgs = TokenContainerProcessor.validateArgs(args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -775,19 +775,19 @@ class TokenNotifier extends _$TokenNotifier with ResultHandler {
Future<void> handleLink(Uri uri) async {
final tokenResults = await TokenImportSchemeProcessor.processUriByAny(uri);
if (tokenResults == null) return;
await handleProcessorResults(tokenResults, {'TokenOriginSourceType': TokenOriginSourceType.link});
await handleProcessorResults(tokenResults, args: {'TokenOriginSourceType': TokenOriginSourceType.link});
}

@override
Future<void> handleProcessorResult(ProcessorResult result, Map<String, dynamic> args) {
Future<void> handleProcessorResult(ProcessorResult result, {Map<String, dynamic> args = const {}}) {
if (result is ProcessorResult<Token>) {
return handleProcessorResults([result], args);
return handleProcessorResults([result], args: args);
}
return Future.value();
}

@override
Future handleProcessorResults(List<ProcessorResult> results, Map<String, dynamic> args) async {
Future handleProcessorResults(List<ProcessorResult> results, {Map<String, dynamic> args = const {}}) async {
final List<ProcessorResult<Token>> tokenResults = results.whereType<ProcessorResult<Token>>().toList();
if (tokenResults.isEmpty) return;
final List<Token> resultTokens = tokenResults.getData();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* privacyIDEA Authenticator
*
* Author: Frank Merkel <frank.merkel@netknights.it>
*
* Copyright (c) 2025 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_riverpod/flutter_riverpod.dart';
import 'package:privacyidea_authenticator/processors/scheme_processors/token_container_processor.dart';
import 'package:privacyidea_authenticator/utils/riverpod/riverpod_providers/generated_providers/token_container_notifier.dart';

import '../../../interfaces/riverpod/state_listeners/state_notifier_provider_listeners/deep_link_listener.dart';
import '../../../model/deeplink.dart';

class TokenContainerDeepLinkListener extends DeepLinkListener {
const TokenContainerDeepLinkListener({
required super.deeplinkProvider,
}) : super(
onNewState: _onNewState,
listenerName: 'TokenContainerDeepLinkListener().processUri',
);

static void _onNewState(WidgetRef ref, AsyncValue<DeepLink>? previous, AsyncValue<DeepLink> next) {
next.whenData((next) async {
final processorResults = await TokenContainerProcessor().processUri(next.uri);
if (processorResults == null || processorResults.isEmpty) return;
ref.read(tokenContainerProvider.notifier).handleProcessorResults(processorResults);
});
}
}
2 changes: 1 addition & 1 deletion lib/utils/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ Future<void> scanQrCode({BuildContext? context, required List<ResultHandler> res
final results = resultHandlerTypeMap[resultHandlerType]!;
final resultHandler = resultHandlerList.firstWhereOrNull((resultHandler) => resultHandlerType.isTypeOf(resultHandler));
if (resultHandler != null) {
await resultHandler.handleProcessorResults(results, {ResultHandler.argTokenOriginSourceType: TokenOriginSourceType.qrScan});
await resultHandler.handleProcessorResults(results, args: {ResultHandler.argTokenOriginSourceType: TokenOriginSourceType.qrScan});
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class _SelectTokensDialogState extends ConsumerState<SelectExportTokensDialog> {
});
}
: (selected, _) {
_showExportDialog(_selectedTokens);
_showExportDialog(selected);
},
),
actions: [
Expand Down
2 changes: 2 additions & 0 deletions lib/widgets/app_wrapper.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import '../utils/riverpod/riverpod_providers/generated_providers/token_notifier.
import '../utils/riverpod/state_listeners/home_widget_deep_link_listener.dart';
import '../utils/riverpod/state_listeners/home_widget_token_state_listener.dart';
import '../utils/riverpod/state_listeners/navigation_deep_link_listener.dart';
import '../utils/riverpod/state_listeners/token_container_deep_link_listener.dart';
import '../utils/riverpod/state_listeners/token_deep_link_listener.dart';
import 'app_wrappers/single_touch_recognizer.dart';
import 'app_wrappers/state_observer.dart';
Expand Down Expand Up @@ -87,6 +88,7 @@ class _AppWrapperState extends ConsumerState<_AppWrapper> {
NavigationDeepLinkListener(deeplinkProvider: deeplinkNotifierProvider),
HomeWidgetDeepLinkListener(deeplinkProvider: deeplinkNotifierProvider),
TokenImportDeepLinkListener(deeplinkProvider: deeplinkNotifierProvider),
TokenContainerDeepLinkListener(deeplinkProvider: deeplinkNotifierProvider),
],
asyncNotifierProviderListeners: [],
child: EasyDynamicThemeWidget(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ void _testTokenContainerNotifier() {
expect(processorResults, isNotNull);
expect(processorResults!.length, 1);
final result = processorResults.first;
await providerContainer.read(tokenContainerProvider.notifier).handleProcessorResult(result, {
await providerContainer.read(tokenContainerProvider.notifier).handleProcessorResult(result, args: {
TokenContainerProcessor.ARG_DO_REPLACE: true,
TokenContainerProcessor.ARG_ADD_DEVICE_INFOS: true,
TokenContainerProcessor.ARG_INIT_SYNC: false,
Expand Down

0 comments on commit 0cf485b

Please sign in to comment.