From 8f16edfd882ae63f7249ad374b809f264fee3c67 Mon Sep 17 00:00:00 2001 From: xavierchanth Date: Wed, 1 Nov 2023 23:03:02 -0400 Subject: [PATCH] refactor: new composition architecture --- .../at_ssh_key_util.dart} | 42 +++--- .../dart_ssh_key_util.dart | 21 ++- .../local_ssh_key_util.dart | 12 +- .../lib/src/common/default_args.dart | 8 +- .../common/{ => mixins}/async_completion.dart | 0 .../{ => mixins}/async_initialization.dart | 0 .../{ => mixins}/at_client_bindings.dart | 2 +- .../noports_core/lib/src/common/types.dart | 8 +- .../brn/new_impl/sshnp_dart_local_impl.dart | 46 ------ .../src/sshnp/brn/sshnp_ssh_key_handler.dart | 48 ------ .../forward_direction/sshnp_forward.dart | 66 --------- .../src/sshnp/impl/sshnp_dart_local_impl.dart | 90 ++++++++++-- .../src/sshnp/impl/sshnp_dart_pure_impl.dart | 98 ++++++++++--- .../lib/src/sshnp/impl/sshnp_exec_impl.dart | 137 ------------------ .../src/sshnp/impl/sshnp_exec_local_impl.dart | 101 +++++++++++++ .../src/sshnp/impl/sshnp_reverse_impl.dart | 115 ++++++++------- .../src/sshnp/impl/sshnp_unsigned_impl.dart | 103 +++++++++++++ .../src/sshnp/impl/sshnp_version_3_impl.dart | 101 ------------- .../config_file_repository.dart | 4 +- .../config_key_repository.dart | 2 +- .../{sshnp_params => models}/sshnp_arg.dart | 18 +-- .../sshnp/{ => models}/sshnp_device_list.dart | 0 .../sshnp_params.dart | 42 +++--- .../src/sshnp/{ => models}/sshnp_result.dart | 0 .../reverse_direction/sshnp_legacy_impl.dart | 97 ------------- .../reverse_direction/sshnp_reverse.dart | 79 ---------- .../reverse_direction/sshnp_reverse_impl.dart | 92 ------------ .../noports_core/lib/src/sshnp/sshnp.dart | 6 +- .../lib/src/sshnp/sshnp_core.dart | 99 ++++++------- .../sshnp_initial_tunnel_handler.dart} | 135 ++++++++++------- .../src/sshnp/util/sshnp_ssh_key_handler.dart | 56 +++++++ .../sshnpd_channel}/sshnpd_channel.dart | 6 +- .../sshnpd_default_channel.dart | 9 +- .../sshnpd_unsigned_channel.dart} | 10 +- .../sshrvd_channel}/sshrvd_channel.dart | 17 ++- .../sshrvd_channel}/sshrvd_dart_channel.dart | 2 +- .../sshrvd_channel}/sshrvd_exec_channel.dart | 2 +- .../noports_core/lib/src/sshnpd/sshnpd.dart | 6 +- .../lib/src/sshnpd/sshnpd_impl.dart | 2 +- .../lib/src/sshnpd/sshnpd_params.dart | 6 +- packages/noports_core/lib/sshnp.dart | 5 +- packages/noports_core/lib/sshnp_core.dart | 3 - .../noports_core/lib/sshnp_foundation.dart | 49 +++++++ packages/noports_core/lib/sshnp_params.dart | 8 +- packages/noports_core/lib/utils.dart | 6 +- .../test/sshnp/sshnp_core_test.dart | 32 +--- .../sshnp/sshnp_params/sshnp_params_test.dart | 86 +++++------ .../profile_actions/profile_run_action.dart | 2 +- .../profile_terminal_action.dart | 2 +- 49 files changed, 835 insertions(+), 1046 deletions(-) rename packages/noports_core/lib/src/common/{ssh_key_utils.dart => at_ssh_key_util/at_ssh_key_util.dart} (77%) rename packages/noports_core/lib/src/common/{ssh_key_utils => at_ssh_key_util}/dart_ssh_key_util.dart (74%) rename packages/noports_core/lib/src/common/{ssh_key_utils => at_ssh_key_util}/local_ssh_key_util.dart (93%) rename packages/noports_core/lib/src/common/{ => mixins}/async_completion.dart (100%) rename packages/noports_core/lib/src/common/{ => mixins}/async_initialization.dart (100%) rename packages/noports_core/lib/src/common/{ => mixins}/at_client_bindings.dart (93%) delete mode 100644 packages/noports_core/lib/src/sshnp/brn/new_impl/sshnp_dart_local_impl.dart delete mode 100644 packages/noports_core/lib/src/sshnp/brn/sshnp_ssh_key_handler.dart delete mode 100644 packages/noports_core/lib/src/sshnp/forward_direction/sshnp_forward.dart delete mode 100644 packages/noports_core/lib/src/sshnp/impl/sshnp_exec_impl.dart create mode 100644 packages/noports_core/lib/src/sshnp/impl/sshnp_exec_local_impl.dart create mode 100644 packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart delete mode 100644 packages/noports_core/lib/src/sshnp/impl/sshnp_version_3_impl.dart rename packages/noports_core/lib/src/sshnp/{sshnp_params => models}/config_file_repository.dart (97%) rename packages/noports_core/lib/src/sshnp/{sshnp_params => models}/config_key_repository.dart (96%) rename packages/noports_core/lib/src/sshnp/{sshnp_params => models}/sshnp_arg.dart (95%) rename packages/noports_core/lib/src/sshnp/{ => models}/sshnp_device_list.dart (100%) rename packages/noports_core/lib/src/sshnp/{sshnp_params => models}/sshnp_params.dart (92%) rename packages/noports_core/lib/src/sshnp/{ => models}/sshnp_result.dart (100%) delete mode 100644 packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_legacy_impl.dart delete mode 100644 packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse.dart delete mode 100644 packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse_impl.dart rename packages/noports_core/lib/src/sshnp/{forward_direction/sshnp_forward_dart.dart => util/sshnp_initial_tunnel_handler.dart} (54%) create mode 100644 packages/noports_core/lib/src/sshnp/util/sshnp_ssh_key_handler.dart rename packages/noports_core/lib/src/sshnp/{channels/sshnpd => util/sshnpd_channel}/sshnpd_channel.dart (97%) rename packages/noports_core/lib/src/sshnp/{channels/sshnpd => util/sshnpd_channel}/sshnpd_default_channel.dart (87%) rename packages/noports_core/lib/src/sshnp/{channels/sshnpd/sshnpd_version_3_channel.dart => util/sshnpd_channel/sshnpd_unsigned_channel.dart} (54%) rename packages/noports_core/lib/src/sshnp/{channels/sshrvd => util/sshrvd_channel}/sshrvd_channel.dart (91%) rename packages/noports_core/lib/src/sshnp/{channels/sshrvd => util/sshrvd_channel}/sshrvd_dart_channel.dart (75%) rename packages/noports_core/lib/src/sshnp/{channels/sshrvd => util/sshrvd_channel}/sshrvd_exec_channel.dart (75%) delete mode 100644 packages/noports_core/lib/sshnp_core.dart create mode 100644 packages/noports_core/lib/sshnp_foundation.dart diff --git a/packages/noports_core/lib/src/common/ssh_key_utils.dart b/packages/noports_core/lib/src/common/at_ssh_key_util/at_ssh_key_util.dart similarity index 77% rename from packages/noports_core/lib/src/common/ssh_key_utils.dart rename to packages/noports_core/lib/src/common/at_ssh_key_util/at_ssh_key_util.dart index 1e4e15541..09e45e976 100644 --- a/packages/noports_core/lib/src/common/ssh_key_utils.dart +++ b/packages/noports_core/lib/src/common/at_ssh_key_util/at_ssh_key_util.dart @@ -2,16 +2,34 @@ import 'dart:async'; import 'dart:convert'; import 'package:dartssh2/dartssh2.dart'; -import 'package:meta/meta.dart'; import 'package:noports_core/sshnp.dart'; import 'package:noports_core/utils.dart'; import 'package:path/path.dart' as path; -export 'ssh_key_utils/dart_ssh_key_util.dart'; -export 'ssh_key_utils/local_ssh_key_util.dart'; +export 'dart_ssh_key_util.dart'; +export 'local_ssh_key_util.dart'; + +abstract interface class AtSshKeyUtil { + FutureOr generateKeyPair({ + required String identifier, + SupportedSshAlgorithm algorithm, + }); + + FutureOr getKeyPair({ + required String identifier, + }); + + FutureOr addKeyPair({ + required AtSshKeyPair keyPair, + required String identifier, + }); + + FutureOr deleteKeyPair({ + required String identifier, + }); +} class AtSshKeyPair { - @protected final SSHKeyPair keyPair; final String identifier; @@ -34,20 +52,4 @@ class AtSshKeyPair { String get privateKeyFileName => identifier; String get publicKeyFileName => '$privateKeyFileName.pub'; - - // TODO consider adding this function - // void destroy() { - // throw UnimplementedError(); - // } -} - -abstract interface class AtSSHKeyUtil { - FutureOr generateKeyPair({ - required String identifier, - SupportedSSHAlgorithm algorithm, - }); - - FutureOr getKeyPair({ - required String identifier, - }); } diff --git a/packages/noports_core/lib/src/common/ssh_key_utils/dart_ssh_key_util.dart b/packages/noports_core/lib/src/common/at_ssh_key_util/dart_ssh_key_util.dart similarity index 74% rename from packages/noports_core/lib/src/common/ssh_key_utils/dart_ssh_key_util.dart rename to packages/noports_core/lib/src/common/at_ssh_key_util/dart_ssh_key_util.dart index 0df82a77d..657788903 100644 --- a/packages/noports_core/lib/src/common/ssh_key_utils/dart_ssh_key_util.dart +++ b/packages/noports_core/lib/src/common/at_ssh_key_util/dart_ssh_key_util.dart @@ -5,19 +5,19 @@ import 'package:cryptography/cryptography.dart'; import 'package:noports_core/utils.dart'; import 'package:openssh_ed25519/openssh_ed25519.dart'; -class DartSSHKeyUtil implements AtSSHKeyUtil { +class DartSshKeyUtil implements AtSshKeyUtil { static final Map _keyPairCache = {}; @override Future generateKeyPair({ required String identifier, - SupportedSSHAlgorithm algorithm = DefaultArgs.sshAlgorithm, + SupportedSshAlgorithm algorithm = DefaultArgs.sshAlgorithm, }) async { AtSshKeyPair keyPair; switch (algorithm) { - case SupportedSSHAlgorithm.rsa: + case SupportedSshAlgorithm.rsa: keyPair = _generateRSAKeyPair(identifier); - case SupportedSSHAlgorithm.ed25519: + case SupportedSshAlgorithm.ed25519: keyPair = await _generateEd25519KeyPair(identifier); } _keyPairCache[identifier] = keyPair; @@ -46,4 +46,17 @@ class DartSSHKeyUtil implements AtSSHKeyUtil { identifier: identifier, ); } + + @override + FutureOr addKeyPair({ + required AtSshKeyPair keyPair, + required String identifier, + }) { + _keyPairCache[identifier] = keyPair; + } + + @override + FutureOr deleteKeyPair({required String identifier}) { + _keyPairCache.remove(identifier); + } } diff --git a/packages/noports_core/lib/src/common/ssh_key_utils/local_ssh_key_util.dart b/packages/noports_core/lib/src/common/at_ssh_key_util/local_ssh_key_util.dart similarity index 93% rename from packages/noports_core/lib/src/common/ssh_key_utils/local_ssh_key_util.dart rename to packages/noports_core/lib/src/common/at_ssh_key_util/local_ssh_key_util.dart index 63bc562a7..04562b4ee 100644 --- a/packages/noports_core/lib/src/common/ssh_key_utils/local_ssh_key_util.dart +++ b/packages/noports_core/lib/src/common/at_ssh_key_util/local_ssh_key_util.dart @@ -6,10 +6,10 @@ import 'package:noports_core/utils.dart'; import 'package:path/path.dart' as path; import 'package:posix/posix.dart' show chmod; -class LocalSshKeyUtil implements AtSSHKeyUtil { +class LocalSshKeyUtil implements AtSshKeyUtil { static const _sshKeygenArgMap = { - SupportedSSHAlgorithm.rsa: ['-t', 'rsa', '-b', '4096'], - SupportedSSHAlgorithm.ed25519: ['-t', 'ed25519', '-a', '100'], + SupportedSshAlgorithm.rsa: ['-t', 'rsa', '-b', '4096'], + SupportedSshAlgorithm.ed25519: ['-t', 'ed25519', '-a', '100'], }; static final Map _keyPairCache = {}; @@ -27,6 +27,8 @@ class LocalSshKeyUtil implements AtSSHKeyUtil { String get _defaultDirectory => sshnpHomeDirectory; + String get username => getUserName(throwIfNull: true)!; + List _filesFromIdentifier({required String identifier}) { return [ File(path.normalize(identifier)), @@ -34,6 +36,7 @@ class LocalSshKeyUtil implements AtSSHKeyUtil { ]; } + @override Future> addKeyPair({ required AtSshKeyPair keyPair, required String identifier, @@ -69,6 +72,7 @@ class LocalSshKeyUtil implements AtSSHKeyUtil { return keyPair; } + @override Future> deleteKeyPair( {required String identifier}) async { var files = _filesFromIdentifier(identifier: identifier); @@ -81,7 +85,7 @@ class LocalSshKeyUtil implements AtSSHKeyUtil { @override Future generateKeyPair({ required String identifier, - SupportedSSHAlgorithm algorithm = DefaultArgs.sshAlgorithm, + SupportedSshAlgorithm algorithm = DefaultArgs.sshAlgorithm, String? directory, String? passphrase, }) async { diff --git a/packages/noports_core/lib/src/common/default_args.dart b/packages/noports_core/lib/src/common/default_args.dart index b935ba6e4..16fb6880e 100644 --- a/packages/noports_core/lib/src/common/default_args.dart +++ b/packages/noports_core/lib/src/common/default_args.dart @@ -5,10 +5,10 @@ import 'package:noports_core/sshrv.dart'; class DefaultArgs { static const String namespace = 'sshnp'; - static const SupportedSSHAlgorithm sshAlgorithm = - SupportedSSHAlgorithm.ed25519; + static const SupportedSshAlgorithm sshAlgorithm = + SupportedSshAlgorithm.ed25519; static const bool verbose = false; - static const SupportedSSHAlgorithm algorithm = SupportedSSHAlgorithm.ed25519; + static const SupportedSshAlgorithm algorithm = SupportedSshAlgorithm.ed25519; static const String rootDomain = 'root.atsign.org'; static const SshrvGenerator sshrvGenerator = SSHRV.exec; static const int localSshdPort = 22; @@ -22,7 +22,7 @@ class DefaultArgs { Platform.isLinux || Platform.isMacOS || Platform.isWindows; } -class DefaultSSHNPArgs { +class DefaultSshnpArgs { static const String device = 'default'; static const int port = 22; static const int localPort = 0; diff --git a/packages/noports_core/lib/src/common/async_completion.dart b/packages/noports_core/lib/src/common/mixins/async_completion.dart similarity index 100% rename from packages/noports_core/lib/src/common/async_completion.dart rename to packages/noports_core/lib/src/common/mixins/async_completion.dart diff --git a/packages/noports_core/lib/src/common/async_initialization.dart b/packages/noports_core/lib/src/common/mixins/async_initialization.dart similarity index 100% rename from packages/noports_core/lib/src/common/async_initialization.dart rename to packages/noports_core/lib/src/common/mixins/async_initialization.dart diff --git a/packages/noports_core/lib/src/common/at_client_bindings.dart b/packages/noports_core/lib/src/common/mixins/at_client_bindings.dart similarity index 93% rename from packages/noports_core/lib/src/common/at_client_bindings.dart rename to packages/noports_core/lib/src/common/mixins/at_client_bindings.dart index 484c1437e..9422be2e1 100644 --- a/packages/noports_core/lib/src/common/at_client_bindings.dart +++ b/packages/noports_core/lib/src/common/mixins/at_client_bindings.dart @@ -1,7 +1,7 @@ import 'package:at_client/at_client.dart'; import 'package:at_utils/at_utils.dart'; -abstract mixin class AtClientBindings { +mixin AtClientBindings { AtClient get atClient; AtSignLogger get logger; diff --git a/packages/noports_core/lib/src/common/types.dart b/packages/noports_core/lib/src/common/types.dart index e77b42525..9e884a412 100644 --- a/packages/noports_core/lib/src/common/types.dart +++ b/packages/noports_core/lib/src/common/types.dart @@ -20,15 +20,15 @@ enum SupportedSshClient { String toString() => _cliArg; } -enum SupportedSSHAlgorithm { +enum SupportedSshAlgorithm { ed25519(cliArg: 'ssh-ed25519'), rsa(cliArg: 'ssh-rsa'); final String _cliArg; - const SupportedSSHAlgorithm({required String cliArg}) : _cliArg = cliArg; + const SupportedSshAlgorithm({required String cliArg}) : _cliArg = cliArg; - factory SupportedSSHAlgorithm.fromString(String cliArg) { - return SupportedSSHAlgorithm.values.firstWhere( + factory SupportedSshAlgorithm.fromString(String cliArg) { + return SupportedSshAlgorithm.values.firstWhere( (arg) => arg._cliArg == cliArg, orElse: () => throw ArgumentError('Unsupported SSH algorithm: $cliArg'), ); diff --git a/packages/noports_core/lib/src/sshnp/brn/new_impl/sshnp_dart_local_impl.dart b/packages/noports_core/lib/src/sshnp/brn/new_impl/sshnp_dart_local_impl.dart deleted file mode 100644 index eebd3f8bf..000000000 --- a/packages/noports_core/lib/src/sshnp/brn/new_impl/sshnp_dart_local_impl.dart +++ /dev/null @@ -1,46 +0,0 @@ -import 'dart:async'; - -import 'package:noports_core/src/sshnp/channels/sshnpd/sshnpd_channel.dart'; -import 'package:noports_core/src/sshnp/channels/sshrvd/sshrvd_channel.dart'; -import 'package:noports_core/src/sshnp/brn/sshnp_ssh_key_handler.dart'; -import 'package:noports_core/src/sshnp/channels/sshnpd/sshnpd_default_channel.dart'; -import 'package:noports_core/src/sshnp/channels/sshrvd/sshrvd_exec_channel.dart'; -import 'package:noports_core/src/sshnp/sshnp_result.dart'; -import 'package:noports_core/sshnp_core.dart'; - -class NewSshnpDartLocalImpl extends SshnpCore with SshnpLocalSSHKeyHandler { - NewSshnpDartLocalImpl({ - required super.atClient, - required super.params, - }); - - @override - SshnpdChannel get sshnpdChannel => SshnpdDefaultChannel( - atClient: atClient, - params: params, - sessionId: sessionId, - namespace: namespace, - ); - - @override - SshrvdChannel get sshrvdChannel => SshrvdExecChannel( - atClient: atClient, - params: params, - sessionId: sessionId, - ); - - @override - Future initialize() async { - if (isSafeToInitialize) { - logger.info('Initializing NewSSHNPDartLocalImpl'); - } - - await super.initialize(); - } - - @override - Future run() async { - //TODO - return SshnpNoOpSuccess(); - } -} diff --git a/packages/noports_core/lib/src/sshnp/brn/sshnp_ssh_key_handler.dart b/packages/noports_core/lib/src/sshnp/brn/sshnp_ssh_key_handler.dart deleted file mode 100644 index 4cfe03f8e..000000000 --- a/packages/noports_core/lib/src/sshnp/brn/sshnp_ssh_key_handler.dart +++ /dev/null @@ -1,48 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:noports_core/src/sshnp/sshnp_core.dart'; -import 'package:noports_core/src/sshnp/sshnp_result.dart'; -import 'package:noports_core/utils.dart'; - -mixin SshnpLocalSSHKeyHandler on SshnpCore { - final LocalSshKeyUtil _sshKeyUtil = LocalSshKeyUtil(); - @override - LocalSshKeyUtil get keyUtil => _sshKeyUtil; - - AtSshKeyPair? _identityKeyPair; - - @override - AtSshKeyPair? get identityKeyPair => _identityKeyPair; - - @override - Future initialize() async { - if (isSafeToInitialize) { - logger.info('Initializing SSHNPLocalSSHKeyHandler'); - - if (!keyUtil.isValidPlatform) { - throw SshnpError( - 'The current platform is not supported: ${Platform.operatingSystem}'); - } - - if (params.identityFile != null) { - logger.info('Loading identity key pair from ${params.identityFile}'); - _identityKeyPair = await keyUtil.getKeyPair( - identifier: params.identityFile!, - passphrase: params.identityPassphrase, - ); - } - } - - /// Make sure we set the keyPair before calling [super.init()] - /// so that the keyPair is available in [SSHNPCore] to share to the daemon - await super.initialize(); - completeInitialization(); - } -} - -mixin SSHNPDartSSHKeyHandler on SshnpCore { - final DartSSHKeyUtil _sshKeyUtil = DartSSHKeyUtil(); - @override - DartSSHKeyUtil get keyUtil => _sshKeyUtil; -} diff --git a/packages/noports_core/lib/src/sshnp/forward_direction/sshnp_forward.dart b/packages/noports_core/lib/src/sshnp/forward_direction/sshnp_forward.dart deleted file mode 100644 index 0fb12d4c7..000000000 --- a/packages/noports_core/lib/src/sshnp/forward_direction/sshnp_forward.dart +++ /dev/null @@ -1,66 +0,0 @@ -import 'dart:async'; - -import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/sshnp/sshnp_core.dart'; -import 'package:noports_core/src/sshnp/sshnp_result.dart'; -import 'package:noports_core/utils.dart'; - -abstract class SSHNPForward extends SshnpCore { - SSHNPForward({ - required super.atClient, - required super.params, - super.shouldInitialize, - }); - - // Direct ssh is only ever done with a sshrvd host - // So we should expect that sshrvdPort is never null - // Hence overriding the getter and setter to make it non-nullable - late int _sshrvdPort; - - @override - int get sshrvdPort => _sshrvdPort; - - @override - set sshrvdPort(int? port) => _sshrvdPort = port!; - - Future requestSocketTunnelFromDaemon() async { - logger.info( - 'Requesting daemon to set up socket tunnel for direct ssh session'); -// send request to the daemon via notification - await notify( - AtKey() - ..key = 'ssh_request' - ..namespace = this.namespace - ..sharedBy = clientAtSign - ..sharedWith = sshnpdAtSign - ..metadata = (Metadata()..ttl = 10000), - signAndWrapAndJsonEncode( - atClient, - { - 'direct': true, - 'sessionId': sessionId, - 'host': host, - 'port': port, - }, - ), - ); - - bool acked = await waitForDaemonResponse(); - if (!acked) { - var error = SshnpError( - 'sshnp timed out: waiting for daemon response\nhint: make sure the device is online', - stackTrace: StackTrace.current); - doneCompleter.completeError(error); - return error; - } - - if (sshnpdAckErrors) { - var error = SshnpError('sshnp failed: with sshnpd acknowledgement errors', - stackTrace: StackTrace.current); - doneCompleter.completeError(error); - return error; - } - - return null; - } -} diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_local_impl.dart index ff05af6df..697e0ee16 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_local_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_local_impl.dart @@ -1,37 +1,101 @@ import 'dart:async'; +import 'package:at_client/at_client.dart'; import 'package:dartssh2/dartssh2.dart'; -import 'package:noports_core/src/sshnp/brn/sshnp_ssh_key_handler.dart'; -import 'package:noports_core/src/sshnp/sshnp_result.dart'; -import 'package:noports_core/sshnp_core.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_initial_tunnel_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_result.dart'; +import 'package:noports_core/sshnp_foundation.dart'; +import 'package:noports_core/utils.dart'; -class SSHNPDartLocalImpl extends SshnpCore with SshnpLocalSSHKeyHandler { - SSHNPDartLocalImpl({ +class SshnpDartLocalImpl extends SshnpCore + with SshnpLocalSshKeyHandler, SshnpDartInitialTunnelHandler { + SshnpDartLocalImpl({ required super.atClient, required super.params, - super.shouldInitialize, }); + @override + SshnpdDefaultChannel get sshnpdChannel => SshnpdDefaultChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + namespace: this.namespace, + ); + + @override + SshrvdChannel get sshrvdChannel => SshrvdDartChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + ); + @override Future initialize() async { - logger.info('Initializing SSHNPForwardDartLocalImpl'); + if (!isSafeToInitialize) return; await super.initialize(); completeInitialization(); } @override Future run() async { - // TODO consider starting the tunnel in a separate isolate - SSHClient client = await startInitialTunnel(); + /// Ensure that sshnp is initialized + await callInitialization(); + + /// Send an ssh request to sshnpd + await notify( + AtKey() + ..key = 'ssh_request' + ..namespace = this.namespace + ..sharedBy = params.clientAtSign + ..sharedWith = params.sshnpdAtSign + ..metadata = (Metadata()..ttl = 10000), + signAndWrapAndJsonEncode(atClient, { + 'direct': true, + 'sessionId': sessionId, + 'host': sshrvdChannel.host, + 'port': sshrvdChannel.port, + }), + ); + + /// Wait for a response from sshnpd + await sshnpdChannel.waitForDaemonResponse(); + + /// Load the ephemeral private key into a key pair + AtSshKeyPair ephemeralKeyPair = AtSshKeyPair.fromPem( + sshnpdChannel.ephemeralPrivateKey, + identifier: 'ephemeral_$sessionId', + directory: keyUtil.sshnpHomeDirectory, + ); + + /// Add the key pair to the key utility + await keyUtil.addKeyPair( + keyPair: ephemeralKeyPair, + identifier: ephemeralKeyPair.identifier, + ); + + /// Start the initial tunnel + SSHClient bean = + await startInitialTunnel(identifier: ephemeralKeyPair.identifier); + + /// Remove the key pair from the key utility + await keyUtil.deleteKeyPair(identifier: ephemeralKeyPair.identifier); + + /// Ensure that we clean up after ourselves + await callDisposal(); - return SshnpCommand( + /// Return the command to be executed externally + return SshnpCommand( localPort: localPort, + host: sshrvdChannel.host, remoteUsername: remoteUsername, - host: 'localhost', - privateKeyFileName: identityKeyPair?.privateKeyFileName, localSshOptions: (params.addForwardsToTunnel) ? null : params.localSshOptions, - connectionBean: client, + privateKeyFileName: identityKeyPair?.identifier, + connectionBean: bean, ); } } diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart index c043d8117..85966b85d 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_dart_pure_impl.dart @@ -1,38 +1,100 @@ import 'dart:async'; +import 'package:at_client/at_client.dart'; import 'package:dartssh2/dartssh2.dart'; -import 'package:noports_core/src/sshnp/brn/sshnp_ssh_key_handler.dart'; -import 'package:noports_core/src/sshnp/sshnp_result.dart'; -import 'package:noports_core/sshnp_core.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_initial_tunnel_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_result.dart'; +import 'package:noports_core/sshnp_foundation.dart'; import 'package:noports_core/utils.dart'; -class SSHNPDartPureImpl extends SshnpCore with SSHNPDartSSHKeyHandler { - final AtSshKeyPair _identityKeyPair; +class SshnpDartPureImpl extends SshnpCore + with SshnpDartSshKeyHandler, SshnpDartInitialTunnelHandler { + SshnpDartPureImpl({ + required super.atClient, + required super.params, + }); @override - AtSshKeyPair get identityKeyPair => _identityKeyPair; + SshnpdDefaultChannel get sshnpdChannel => SshnpdDefaultChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + namespace: this.namespace, + ); - SSHNPDartPureImpl({ - required super.atClient, - required super.params, - required AtSshKeyPair identityKeyPair, - super.shouldInitialize, - }) : _identityKeyPair = identityKeyPair; + @override + SshrvdChannel get sshrvdChannel => SshrvdDartChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + ); @override Future initialize() async { - logger.info('Initializing SSHNPForwardDartPureImpl'); + if (!isSafeToInitialize) return; await super.initialize(); completeInitialization(); } @override Future run() async { - SSHClient client = await startInitialTunnel(); - // Todo: consider returning a SSHNPCommand instead of a SSHNPNoOpSuccess - return SshnpNoOpSuccess( - message: 'Connection established:\n$terminateMessage', - connectionBean: client, + /// Ensure that sshnp is initialized + await callInitialization(); + + /// Send an ssh request to sshnpd + await notify( + AtKey() + ..key = 'ssh_request' + ..namespace = this.namespace + ..sharedBy = params.clientAtSign + ..sharedWith = params.sshnpdAtSign + ..metadata = (Metadata()..ttl = 10000), + signAndWrapAndJsonEncode(atClient, { + 'direct': true, + 'sessionId': sessionId, + 'host': sshrvdChannel.host, + 'port': sshrvdChannel.port, + }), + ); + + /// Wait for a response from sshnpd + await sshnpdChannel.waitForDaemonResponse(); + + /// Load the ephemeral private key into a key pair + AtSshKeyPair ephemeralKeyPair = AtSshKeyPair.fromPem( + sshnpdChannel.ephemeralPrivateKey, + identifier: 'ephemeral_$sessionId', + ); + + /// Add the key pair to the key utility + await keyUtil.addKeyPair( + keyPair: ephemeralKeyPair, + identifier: ephemeralKeyPair.identifier, + ); + + /// Start the initial tunnel + SSHClient bean = + await startInitialTunnel(identifier: ephemeralKeyPair.identifier); + + /// Remove the key pair from the key utility + await keyUtil.deleteKeyPair(identifier: ephemeralKeyPair.identifier); + + /// Ensure that we clean up after ourselves + await callDisposal(); + + /// Return the command to be executed externally + return SshnpCommand( + localPort: localPort, + host: sshrvdChannel.host, + remoteUsername: remoteUsername, + localSshOptions: + (params.addForwardsToTunnel) ? null : params.localSshOptions, + privateKeyFileName: identityKeyPair?.identifier, + connectionBean: bean, ); } } diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_exec_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_exec_impl.dart deleted file mode 100644 index ec76b6744..000000000 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_exec_impl.dart +++ /dev/null @@ -1,137 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; - -import 'package:at_client/at_client.dart' hide StringBuffer; - -import 'package:noports_core/src/sshnp/mixins/sshnpd_payload_handler.dart'; -import 'package:noports_core/src/sshnp/brn/sshnp_ssh_key_handler.dart'; -import 'package:noports_core/src/sshnp/sshnp_core.dart'; -import 'package:noports_core/sshnp.dart'; -import 'package:noports_core/utils.dart'; - -class SSHNPExecImpl extends SshnpCore - with SshnpLocalSSHKeyHandler, SSHNPDDefaultPayloadHandler { - late AtSshKeyPair ephemeralKeyPair; - - SSHNPExecImpl({ - required AtClient atClient, - required SshnpParams params, - bool? shouldInitialize, - }) : super( - atClient: atClient, - params: params, - shouldInitialize: shouldInitialize, - ); - - @override - Future run() async { - await callInitialization(); - - var error = await requestSocketTunnelFromDaemon(); - if (error != null) { - return error; - } - - ephemeralKeyPair = AtSshKeyPair.fromPem( - ephemeralPrivateKey, - identifier: 'ephemeral_$sessionId', - directory: keyUtil.sshnpHomeDirectory, - ); - - logger.info( - 'Starting direct ssh session to $host on port $sshrvdPort with forwardLocal of $localPort'); - - try { - String? errorMessage; - Process? process; - - await keyUtil.addKeyPair( - keyPair: ephemeralKeyPair, - identifier: ephemeralKeyPair.identifier, - ); - - String argsString = '$remoteUsername@$host' - ' -p $sshrvdPort' - ' -i ${ephemeralKeyPair.privateKeyFileName}' - ' -L $localPort:localhost:${params.remoteSshdPort}' - ' -o LogLevel=VERBOSE' - ' -t -t' - ' -o StrictHostKeyChecking=accept-new' - ' -o IdentitiesOnly=yes' - ' -o BatchMode=yes' - ' -o ExitOnForwardFailure=yes' - ' -f' // fork after authentication - this is important - ; - if (params.addForwardsToTunnel) { - argsString += ' ${params.localSshOptions.join(' ')}'; - } - argsString += ' sleep 15'; - - List args = argsString.split(' '); - - logger.info('$sessionId | Executing /usr/bin/ssh ${args.join(' ')}'); - - // Because of the options we are using, we can wait for this process - // to complete, because it will exit with exitCode 0 once it has connected - // successfully - late int sshExitCode; - final soutBuf = StringBuffer(); - final serrBuf = StringBuffer(); - try { - process = await Process.start('/usr/bin/ssh', args); - process.stdout.transform(Utf8Decoder()).listen((String s) { - soutBuf.write(s); - logger.info('$sessionId | sshStdOut | $s'); - }, onError: (e) {}); - process.stderr.transform(Utf8Decoder()).listen((String s) { - serrBuf.write(s); - logger.info('$sessionId | sshStdErr | $s'); - }, onError: (e) {}); - sshExitCode = await process.exitCode.timeout(Duration(seconds: 10)); - // ignore: unused_catch_clause - } on TimeoutException catch (e) { - sshExitCode = 6464; - } - - await keyUtil.deleteKeyPair( - identifier: ephemeralKeyPair.identifier, - ); - - if (sshExitCode != 0) { - if (sshExitCode == 6464) { - logger.shout( - '$sessionId | Command timed out: /usr/bin/ssh ${args.join(' ')}'); - errorMessage = 'Failed to establish connection - timed out'; - } else { - logger.shout('$sessionId | Exit code $sshExitCode from' - ' /usr/bin/ssh ${args.join(' ')}'); - errorMessage = - 'Failed to establish connection - exit code $sshExitCode'; - } - throw SshnpError(errorMessage); - } - - doneCompleter.complete(); - return SshnpCommand( - localPort: localPort, - remoteUsername: remoteUsername, - host: 'localhost', - privateKeyFileName: identityKeyPair?.privateKeyFileName, - localSshOptions: - (params.addForwardsToTunnel) ? null : params.localSshOptions, - connectionBean: process, - ); - } on SshnpError catch (e) { - doneCompleter.completeError(e, e.stackTrace); - return e; - } catch (e, s) { - doneCompleter.completeError(e, s); - return SshnpError( - 'SSH Client failure : $e', - error: e, - stackTrace: s, - ); - } - } -} diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_exec_local_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_exec_local_impl.dart new file mode 100644 index 000000000..529e1c2b9 --- /dev/null +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_exec_local_impl.dart @@ -0,0 +1,101 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:at_client/at_client.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_initial_tunnel_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_result.dart'; +import 'package:noports_core/sshnp_foundation.dart'; +import 'package:noports_core/utils.dart'; + +class SshnpExecLocalImpl extends SshnpCore + with SshnpLocalSshKeyHandler, SshnpExecInitialTunnelHandler { + SshnpExecLocalImpl({ + required super.atClient, + required super.params, + }); + + @override + SshnpdDefaultChannel get sshnpdChannel => SshnpdDefaultChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + namespace: this.namespace, + ); + + @override + SshrvdChannel get sshrvdChannel => SshrvdExecChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + ); + + @override + Future initialize() async { + if (!isSafeToInitialize) return; + await super.initialize(); + completeInitialization(); + } + + @override + Future run() async { + /// Ensure that sshnp is initialized + await callInitialization(); + + /// Send an ssh request to sshnpd + await notify( + AtKey() + ..key = 'ssh_request' + ..namespace = this.namespace + ..sharedBy = params.clientAtSign + ..sharedWith = params.sshnpdAtSign + ..metadata = (Metadata()..ttl = 10000), + signAndWrapAndJsonEncode(atClient, { + 'direct': true, + 'sessionId': sessionId, + 'host': sshrvdChannel.host, + 'port': sshrvdChannel.port, + }), + ); + + /// Wait for a response from sshnpd + await sshnpdChannel.waitForDaemonResponse(); + + /// Load the ephemeral private key into a key pair + AtSshKeyPair ephemeralKeyPair = AtSshKeyPair.fromPem( + sshnpdChannel.ephemeralPrivateKey, + identifier: 'ephemeral_$sessionId', + directory: keyUtil.sshnpHomeDirectory, + ); + + /// Add the key pair to the key utility + await keyUtil.addKeyPair( + keyPair: ephemeralKeyPair, + identifier: ephemeralKeyPair.identifier, + ); + + /// Start the initial tunnel + Process bean = + await startInitialTunnel(identifier: ephemeralKeyPair.identifier); + + /// Remove the key pair from the key utility + await keyUtil.deleteKeyPair(identifier: ephemeralKeyPair.identifier); + + /// Ensure that we clean up after ourselves + await callDisposal(); + + /// Return the command to be executed externally + return SshnpCommand( + localPort: localPort, + host: sshrvdChannel.host, + remoteUsername: remoteUsername, + localSshOptions: + (params.addForwardsToTunnel) ? null : params.localSshOptions, + privateKeyFileName: identityKeyPair?.identifier, + connectionBean: bean, + ); + } +} diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_reverse_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_reverse_impl.dart index df98185e9..77eab808b 100644 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_reverse_impl.dart +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_reverse_impl.dart @@ -1,94 +1,103 @@ import 'dart:async'; import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/common/validation_utils.dart'; -import 'package:noports_core/src/sshnp/mixins/sshnpd_payload_handler.dart'; -import 'package:noports_core/src/sshnp/reverse_direction/sshnp_reverse.dart'; -import 'package:noports_core/sshnp.dart'; -import 'package:noports_core/sshrv.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_result.dart'; +import 'package:noports_core/sshnp_foundation.dart'; +import 'package:noports_core/utils.dart'; -class SSHNPReverseImpl extends SSHNPReverse with SSHNPDDefaultPayloadHandler { - SSHNPReverseImpl({ - required AtClient atClient, - required SshnpParams params, - SshrvGenerator? sshrvGenerator, - bool? shouldInitialize, - }) : super( - atClient: atClient, - params: params, - sshrvGenerator: sshrvGenerator, - shouldInitialize: shouldInitialize, - ); +class SshnpReverseImpl extends SshnpCore with SshnpLocalSshKeyHandler { + SshnpReverseImpl({ + required super.atClient, + required super.params, + }); + + @override + SshnpdDefaultChannel get sshnpdChannel => SshnpdDefaultChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + namespace: this.namespace, + ); + + @override + SshrvdExecChannel get sshrvdChannel => SshrvdExecChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + ); + + late final AtSshKeyPair ephemeralKeyPair; @override Future initialize() async { - logger.info('Initializing SSHNPReverseImpl'); + if (!isSafeToInitialize) return; await super.initialize(); + + /// Generate an ephemeral key pair for this session + ephemeralKeyPair = await keyUtil.generateKeyPair( + identifier: 'ephemeral_$sessionId', + directory: keyUtil.sshnpHomeDirectory, + ); + + /// Authorize the public key so sshnpd can connect to us + await keyUtil.authorizePublicKey( + sshPublicKey: ephemeralKeyPair.publicKeyContents, + localSshdPort: params.localSshdPort, + sessionId: sessionId, + ); + completeInitialization(); } @override Future run() async { + /// Ensure that sshnp is initialized await callInitialization(); - logger.info('Requesting daemon to start reverse ssh session'); + /// Start sshrv + var bean = await sshrvdChannel.runSshrv(); - Future? sshrvResult; - if (usingSshrv) { - // Connect to rendezvous point using background process. - // sshnp (this program) can then exit without issue. - SSHRV sshrv = sshrvGenerator(host, sshrvdPort!, - localSshdPort: params.localSshdPort); - sshrvResult = sshrv.run(); - } - // send request to the daemon via notification + /// Send a reverse sshdrequest to sshnpd + /// This will notify it that it can now connect to us await notify( AtKey() ..key = 'ssh_request' ..namespace = this.namespace - ..sharedBy = clientAtSign - ..sharedWith = sshnpdAtSign - ..metadata = (Metadata() - ..ttr = -1 - ..ttl = 10000), + ..sharedBy = params.clientAtSign + ..sharedWith = params.sshnpdAtSign + ..metadata = (Metadata()..ttl = 10000), signAndWrapAndJsonEncode( atClient, { 'direct': false, 'sessionId': sessionId, - 'host': host, - 'port': port, - 'username': localUsername, + 'host': sshrvdChannel.host, + 'port': sshrvdChannel.port, + 'username': keyUtil.username, 'remoteForwardPort': localPort, 'privateKey': ephemeralKeyPair.privateKeyContents, }, ), ); - bool acked = await waitForDaemonResponse(); - if (!acked) { - var error = - SshnpError('sshnp connection timeout: waiting for daemon response'); - doneCompleter.completeError(error); - return error; - } + /// Wait for a response from sshnpd + await sshnpdChannel.waitForDaemonResponse(); - if (sshnpdAckErrors) { - var error = - SshnpError('sshnp failed: with sshnpd acknowledgement errors'); - doneCompleter.completeError(error); - return error; - } + /// Ensure that we clean up after ourselves + await callDisposal(); - doneCompleter.complete(); + /// Return the command to be executed externally return SshnpCommand( localPort: localPort, + host: sshrvdChannel.host, remoteUsername: remoteUsername, - host: 'localhost', - privateKeyFileName: identityKeyPair?.privateKeyFileName, localSshOptions: (params.addForwardsToTunnel) ? null : params.localSshOptions, - connectionBean: sshrvResult, + privateKeyFileName: identityKeyPair?.identifier, + connectionBean: bean, ); } } diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart new file mode 100644 index 000000000..93b98de11 --- /dev/null +++ b/packages/noports_core/lib/src/sshnp/impl/sshnp_unsigned_impl.dart @@ -0,0 +1,103 @@ +import 'dart:async'; + +import 'package:at_client/at_client.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_result.dart'; +import 'package:noports_core/sshnp_foundation.dart'; +import 'package:noports_core/utils.dart'; + +class SshnpUnsignedImpl extends SshnpCore + with SshnpLocalSshKeyHandler { + SshnpUnsignedImpl({ + required super.atClient, + required super.params, + }); + + @override + SshnpdDefaultChannel get sshnpdChannel => SshnpdDefaultChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + namespace: this.namespace, + ); + + @override + SshrvdExecChannel get sshrvdChannel => SshrvdExecChannel( + atClient: atClient, + params: params, + sessionId: sessionId, + ); + + @override + Future initialize() async { + if (!isSafeToInitialize) return; + await super.initialize(); + + /// Generate an ephemeral key pair for this session + AtSshKeyPair ephemeralKeyPair = await keyUtil.generateKeyPair( + identifier: 'ephemeral_$sessionId', + directory: keyUtil.sshnpHomeDirectory, + ); + + /// Authorize the public key so sshnpd can connect to us + await keyUtil.authorizePublicKey( + sshPublicKey: ephemeralKeyPair.publicKeyContents, + localSshdPort: params.localSshdPort, + sessionId: sessionId, + ); + + /// Share our private key with sshnpd so it can connect to us + AtKey sendOurPrivateKeyToSshnpd = AtKey() + ..key = 'privatekey' + ..sharedBy = params.clientAtSign + ..sharedWith = params.sshnpdAtSign + ..namespace = this.namespace + ..metadata = (Metadata()..ttl = 10000); + await notify( + sendOurPrivateKeyToSshnpd, + ephemeralKeyPair.privateKeyContents, + ); + + completeInitialization(); + } + + @override + Future run() async { + /// Ensure that sshnp is initialized + await callInitialization(); + + /// Start sshrv + var bean = await sshrvdChannel.runSshrv(); + + /// Send an sshd request to sshnpd + /// This will notify it that it can now connect to us + await notify( + AtKey() + ..key = 'sshd' + ..namespace = this.namespace + ..sharedBy = params.clientAtSign + ..sharedWith = params.sshnpdAtSign + ..metadata = (Metadata()..ttl = 10000), + '$localPort ${sshrvdChannel.port} ${keyUtil.username} ${sshrvdChannel.host} $sessionId', + ); + + /// Wait for a response from sshnpd + await sshnpdChannel.waitForDaemonResponse(); + + /// Ensure that we clean up after ourselves + await callDisposal(); + + /// Return the command to be executed externally + return SshnpCommand( + localPort: localPort, + host: sshrvdChannel.host, + remoteUsername: remoteUsername, + localSshOptions: + (params.addForwardsToTunnel) ? null : params.localSshOptions, + privateKeyFileName: identityKeyPair?.identifier, + connectionBean: bean, + ); + } +} diff --git a/packages/noports_core/lib/src/sshnp/impl/sshnp_version_3_impl.dart b/packages/noports_core/lib/src/sshnp/impl/sshnp_version_3_impl.dart deleted file mode 100644 index 701f37cc8..000000000 --- a/packages/noports_core/lib/src/sshnp/impl/sshnp_version_3_impl.dart +++ /dev/null @@ -1,101 +0,0 @@ -import 'dart:async'; - -import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/sshnp/mixins/sshnpd_payload_handler.dart'; -import 'package:noports_core/src/sshnp/reverse_direction/sshnp_reverse.dart'; -import 'package:noports_core/sshnp.dart'; -import 'package:noports_core/sshrv.dart'; - -class SSHNPVersion3Impl extends SSHNPReverse with SSHNPDVersion3PayloadHandler { - SSHNPVersion3Impl({ - required AtClient atClient, - required SshnpParams params, - SshrvGenerator? sshrvGenerator, - bool? shouldInitialize, - }) : super( - atClient: atClient, - params: params, - sshrvGenerator: sshrvGenerator, - shouldInitialize: shouldInitialize, - ); - - @override - Future initialize() async { - logger.info('Initializing SSHNPLegacyImpl'); - await super.initialize(); - if (!isSafeToInitialize) return; - - // Share our private key with sshnpd - AtKey sendOurPrivateKeyToSshnpd = AtKey() - ..key = 'privatekey' - ..sharedBy = clientAtSign - ..sharedWith = sshnpdAtSign - ..namespace = this.namespace - ..metadata = (Metadata() - ..ttr = -1 - ..ttl = 10000); - await notify( - sendOurPrivateKeyToSshnpd, ephemeralKeyPair.privateKeyContents); - - completeInitialization(); - } - - @override - Future run() async { - await callInitialization(); - - logger.info('Requesting legacy daemon to start reverse ssh session'); - - Future? sshrvResult; - if (usingSshrv) { - // Connect to rendezvous point using background process. - // sshnp (this program) can then exit without issue. - SSHRV sshrv = sshrvGenerator(host, sshrvdPort!, - localSshdPort: params.localSshdPort); - sshrvResult = sshrv.run(); - } - - // send request to the daemon via notification - await notify( - AtKey() - ..key = 'sshd' - ..namespace = this.namespace - ..sharedBy = clientAtSign - ..sharedWith = sshnpdAtSign - ..metadata = (Metadata() - ..ttr = -1 - ..ttl = 10000), - '$localPort $port $localUsername $host $sessionId', - ); - - bool acked = await waitForDaemonResponse(); - if (!acked) { - var error = SshnpError( - 'sshnp timed out: waiting for daemon response\nhint: make sure the device is online', - stackTrace: StackTrace.current, - ); - doneCompleter.completeError(error); - return error; - } - - if (sshnpdAckErrors) { - var error = SshnpError( - 'sshnp failed: with sshnpd acknowledgement errors', - stackTrace: StackTrace.current, - ); - doneCompleter.completeError(error); - return error; - } - - doneCompleter.complete(); - return SshnpCommand( - localPort: localPort, - remoteUsername: remoteUsername, - host: 'localhost', - privateKeyFileName: identityKeyPair?.privateKeyFileName, - localSshOptions: - (params.addForwardsToTunnel) ? null : params.localSshOptions, - connectionBean: sshrvResult, - ); - } -} diff --git a/packages/noports_core/lib/src/sshnp/sshnp_params/config_file_repository.dart b/packages/noports_core/lib/src/sshnp/models/config_file_repository.dart similarity index 97% rename from packages/noports_core/lib/src/sshnp/sshnp_params/config_file_repository.dart rename to packages/noports_core/lib/src/sshnp/models/config_file_repository.dart index ab88ad055..09b3fda41 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_params/config_file_repository.dart +++ b/packages/noports_core/lib/src/sshnp/models/config_file_repository.dart @@ -1,8 +1,8 @@ import 'dart:io'; import 'package:noports_core/src/common/file_system_utils.dart'; -import 'package:noports_core/src/sshnp/sshnp_params/sshnp_params.dart'; -import 'package:noports_core/src/sshnp/sshnp_params/sshnp_arg.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_params.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_arg.dart'; import 'package:path/path.dart' as path; class ConfigFileRepository { diff --git a/packages/noports_core/lib/src/sshnp/sshnp_params/config_key_repository.dart b/packages/noports_core/lib/src/sshnp/models/config_key_repository.dart similarity index 96% rename from packages/noports_core/lib/src/sshnp/sshnp_params/config_key_repository.dart rename to packages/noports_core/lib/src/sshnp/models/config_key_repository.dart index c8e8f5075..26f0c1067 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_params/config_key_repository.dart +++ b/packages/noports_core/lib/src/sshnp/models/config_key_repository.dart @@ -1,7 +1,7 @@ import 'package:at_client/at_client.dart'; import 'package:meta/meta.dart'; import 'package:noports_core/src/common/default_args.dart'; -import 'package:noports_core/src/sshnp/sshnp_params/sshnp_params.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_params.dart'; class ConfigKeyRepository { @visibleForTesting diff --git a/packages/noports_core/lib/src/sshnp/sshnp_params/sshnp_arg.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart similarity index 95% rename from packages/noports_core/lib/src/sshnp/sshnp_params/sshnp_arg.dart rename to packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart index 73c539034..c898178e8 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_params/sshnp_arg.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -216,7 +216,7 @@ class SshnpArg { name: 'device', abbr: 'd', help: 'Receiving device name', - defaultsTo: DefaultSSHNPArgs.device, + defaultsTo: DefaultSshnpArgs.device, ); static const hostArg = SshnpArg( name: 'host', @@ -229,7 +229,7 @@ class SshnpArg { abbr: 'p', help: 'TCP port to connect back to (only required if --host specified a FQDN/IP)', - defaultsTo: DefaultSSHNPArgs.port, + defaultsTo: DefaultSshnpArgs.port, type: ArgType.integer, ); static const localPortArg = SshnpArg( @@ -237,7 +237,7 @@ class SshnpArg { abbr: 'l', help: 'Reverse ssh port to listen on, on your local machine, by sshnp default finds a spare port', - defaultsTo: DefaultSSHNPArgs.localPort, + defaultsTo: DefaultSshnpArgs.localPort, type: ArgType.integer, ); static const identityFileArg = SshnpArg( @@ -256,13 +256,13 @@ class SshnpArg { abbr: 's', help: 'When true, the ssh public key will be sent to the remote host for use in the ssh session', - defaultsTo: DefaultSSHNPArgs.sendSshPublicKey, + defaultsTo: DefaultSshnpArgs.sendSshPublicKey, format: ArgFormat.flag, ); static const localSshOptionsArg = SshnpArg( name: 'local-ssh-options', abbr: 'o', - defaultsTo: DefaultSSHNPArgs.localSshOptions, + defaultsTo: DefaultSshnpArgs.localSshOptions, help: 'Add these commands to the local ssh command', format: ArgFormat.multiOption, ); @@ -297,7 +297,7 @@ class SshnpArg { static const legacyDaemonArg = SshnpArg( name: 'legacy-daemon', help: 'Request is to a legacy (< 4.0.0) noports daemon', - defaultsTo: DefaultSSHNPArgs.legacyDaemon, + defaultsTo: DefaultSshnpArgs.legacyDaemon, format: ArgFormat.flag, ); static const remoteSshdPortArg = SshnpArg( @@ -321,7 +321,7 @@ class SshnpArg { static final sshClientArg = SshnpArg( name: 'ssh-client', help: 'What to use for outbound ssh connections', - defaultsTo: DefaultSSHNPArgs.sshClient.toString(), + defaultsTo: DefaultSshnpArgs.sshClient.toString(), allowed: SupportedSshClient.values.map((c) => c.toString()).toList(), parseWhen: ParseWhen.commandLine, ); @@ -329,7 +329,7 @@ class SshnpArg { name: 'ssh-algorithm', help: 'SSH algorithm to use', defaultsTo: DefaultArgs.sshAlgorithm.toString(), - allowed: SupportedSSHAlgorithm.values.map((c) => c.toString()).toList(), + allowed: SupportedSshAlgorithm.values.map((c) => c.toString()).toList(), parseWhen: ParseWhen.commandLine, ); static const addForwardsToTunnelArg = SshnpArg( @@ -349,7 +349,7 @@ class SshnpArg { static const listDevicesArg = SshnpArg( name: 'list-devices', help: 'List available devices', - defaultsTo: DefaultSSHNPArgs.listDevices, + defaultsTo: DefaultSshnpArgs.listDevices, aliases: ['ls'], negatable: false, parseWhen: ParseWhen.commandLine, diff --git a/packages/noports_core/lib/src/sshnp/sshnp_device_list.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_device_list.dart similarity index 100% rename from packages/noports_core/lib/src/sshnp/sshnp_device_list.dart rename to packages/noports_core/lib/src/sshnp/models/sshnp_device_list.dart diff --git a/packages/noports_core/lib/src/sshnp/sshnp_params/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart similarity index 92% rename from packages/noports_core/lib/src/sshnp/sshnp_params/sshnp_params.dart rename to packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 27bfe03c1..3c59e878e 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_params/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -1,8 +1,8 @@ import 'dart:convert'; import 'package:noports_core/src/common/types.dart'; -import 'package:noports_core/src/sshnp/sshnp_params/config_file_repository.dart'; -import 'package:noports_core/src/sshnp/sshnp_params/sshnp_arg.dart'; +import 'package:noports_core/src/sshnp/models/config_file_repository.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_arg.dart'; import 'package:noports_core/src/common/default_args.dart'; import 'package:noports_core/sshnp.dart'; @@ -34,7 +34,7 @@ class SshnpParams { final bool addForwardsToTunnel; final String? atKeysFilePath; final SupportedSshClient sshClient; - final SupportedSSHAlgorithm sshAlgorithm; + final SupportedSshAlgorithm sshAlgorithm; /// Special Arguments final String? @@ -48,24 +48,24 @@ class SshnpParams { required this.sshnpdAtSign, required this.host, this.profileName, - this.device = DefaultSSHNPArgs.device, - this.port = DefaultSSHNPArgs.port, - this.localPort = DefaultSSHNPArgs.localPort, + this.device = DefaultSshnpArgs.device, + this.port = DefaultSshnpArgs.port, + this.localPort = DefaultSshnpArgs.localPort, this.identityFile, this.identityPassphrase, - this.sendSshPublicKey = DefaultSSHNPArgs.sendSshPublicKey, - this.localSshOptions = DefaultSSHNPArgs.localSshOptions, + this.sendSshPublicKey = DefaultSshnpArgs.sendSshPublicKey, + this.localSshOptions = DefaultSshnpArgs.localSshOptions, this.verbose = DefaultArgs.verbose, this.remoteUsername, this.atKeysFilePath, this.rootDomain = DefaultArgs.rootDomain, this.localSshdPort = DefaultArgs.localSshdPort, - this.legacyDaemon = DefaultSSHNPArgs.legacyDaemon, - this.listDevices = DefaultSSHNPArgs.listDevices, + this.legacyDaemon = DefaultSshnpArgs.legacyDaemon, + this.listDevices = DefaultSshnpArgs.listDevices, this.remoteSshdPort = DefaultArgs.remoteSshdPort, this.idleTimeout = DefaultArgs.idleTimeout, this.addForwardsToTunnel = DefaultArgs.addForwardsToTunnel, - this.sshClient = DefaultSSHNPArgs.sshClient, + this.sshClient = DefaultSshnpArgs.sshClient, this.sshAlgorithm = DefaultArgs.sshAlgorithm, }); @@ -128,27 +128,27 @@ class SshnpParams { clientAtSign: partial.clientAtSign!, sshnpdAtSign: partial.sshnpdAtSign!, host: partial.host!, - device: partial.device ?? DefaultSSHNPArgs.device, - port: partial.port ?? DefaultSSHNPArgs.port, - localPort: partial.localPort ?? DefaultSSHNPArgs.localPort, + device: partial.device ?? DefaultSshnpArgs.device, + port: partial.port ?? DefaultSshnpArgs.port, + localPort: partial.localPort ?? DefaultSshnpArgs.localPort, identityFile: partial.identityFile, identityPassphrase: partial.identityPassphrase, sendSshPublicKey: - partial.sendSshPublicKey ?? DefaultSSHNPArgs.sendSshPublicKey, + partial.sendSshPublicKey ?? DefaultSshnpArgs.sendSshPublicKey, localSshOptions: - partial.localSshOptions ?? DefaultSSHNPArgs.localSshOptions, + partial.localSshOptions ?? DefaultSshnpArgs.localSshOptions, verbose: partial.verbose ?? DefaultArgs.verbose, remoteUsername: partial.remoteUsername, atKeysFilePath: partial.atKeysFilePath, rootDomain: partial.rootDomain ?? DefaultArgs.rootDomain, localSshdPort: partial.localSshdPort ?? DefaultArgs.localSshdPort, - listDevices: partial.listDevices ?? DefaultSSHNPArgs.listDevices, - legacyDaemon: partial.legacyDaemon ?? DefaultSSHNPArgs.legacyDaemon, + listDevices: partial.listDevices ?? DefaultSshnpArgs.listDevices, + legacyDaemon: partial.legacyDaemon ?? DefaultSshnpArgs.legacyDaemon, remoteSshdPort: partial.remoteSshdPort ?? DefaultArgs.remoteSshdPort, idleTimeout: partial.idleTimeout ?? DefaultArgs.idleTimeout, addForwardsToTunnel: partial.addForwardsToTunnel ?? DefaultArgs.addForwardsToTunnel, - sshClient: partial.sshClient ?? DefaultSSHNPArgs.sshClient, + sshClient: partial.sshClient ?? DefaultSshnpArgs.sshClient, sshAlgorithm: partial.sshAlgorithm ?? DefaultArgs.sshAlgorithm, ); } @@ -236,7 +236,7 @@ class SshnpPartialParams { final int? idleTimeout; final bool? addForwardsToTunnel; final SupportedSshClient? sshClient; - final SupportedSSHAlgorithm? sshAlgorithm; + final SupportedSshAlgorithm? sshAlgorithm; /// Operation flags final bool? listDevices; @@ -352,7 +352,7 @@ class SshnpPartialParams { : SupportedSshClient.fromString(args[SshnpArg.sshClientArg.name]), sshAlgorithm: args[SshnpArg.sshAlgorithmArg.name] == null ? null - : SupportedSSHAlgorithm.fromString( + : SupportedSshAlgorithm.fromString( args[SshnpArg.sshAlgorithmArg.name]), ); } diff --git a/packages/noports_core/lib/src/sshnp/sshnp_result.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_result.dart similarity index 100% rename from packages/noports_core/lib/src/sshnp/sshnp_result.dart rename to packages/noports_core/lib/src/sshnp/models/sshnp_result.dart diff --git a/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_legacy_impl.dart b/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_legacy_impl.dart deleted file mode 100644 index 9e141f4c4..000000000 --- a/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_legacy_impl.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'dart:async'; - -import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/sshnp/mixins/sshnpd_payload_handler.dart'; -import 'package:noports_core/src/sshnp/reverse_direction/sshnp_reverse.dart'; -import 'package:noports_core/sshnp.dart'; -import 'package:noports_core/sshrv.dart'; - -class SSHNPLegacyImpl extends SSHNPReverse with SSHNPDVersion3PayloadHandler { - SSHNPLegacyImpl({ - required AtClient atClient, - required SshnpParams params, - SshrvGenerator? sshrvGenerator, - bool? shouldInitialize, - }) : super( - atClient: atClient, - params: params, - sshrvGenerator: sshrvGenerator, - shouldInitialize: shouldInitialize, - ); - - @override - Future init() async { - logger.info('Initializing SSHNPLegacyImpl'); - await super.init(); - if (initializedCompleter.isCompleted) return; - - // Share our private key with sshnpd - AtKey sendOurPrivateKeyToSshnpd = AtKey() - ..key = 'privatekey' - ..sharedBy = clientAtSign - ..sharedWith = sshnpdAtSign - ..namespace = this.namespace - ..metadata = (Metadata()..ttl = 10000); - await notify( - sendOurPrivateKeyToSshnpd, ephemeralKeyPair.privateKeyContents); - - completeInitialization(); - } - - @override - Future run() async { - await startAndWaitForInit(); - - logger.info('Requesting legacy daemon to start reverse ssh session'); - - Future? sshrvResult; - if (usingSshrv) { - // Connect to rendezvous point using background process. - // sshnp (this program) can then exit without issue. - SSHRV sshrv = sshrvGenerator(host, sshrvdPort!, - localSshdPort: params.localSshdPort); - sshrvResult = sshrv.run(); - } - - // send request to the daemon via notification - await notify( - AtKey() - ..key = 'sshd' - ..namespace = this.namespace - ..sharedBy = clientAtSign - ..sharedWith = sshnpdAtSign - ..metadata = (Metadata()..ttl = 10000), - '$localPort $port $localUsername $host $sessionId', - ); - - bool acked = await waitForDaemonResponse(); - if (!acked) { - var error = SshnpError( - 'sshnp timed out: waiting for daemon response\nhint: make sure the device is online', - stackTrace: StackTrace.current, - ); - doneCompleter.completeError(error); - return error; - } - - if (sshnpdAckErrors) { - var error = SshnpError( - 'sshnp failed: with sshnpd acknowledgement errors', - stackTrace: StackTrace.current, - ); - doneCompleter.completeError(error); - return error; - } - - doneCompleter.complete(); - return SshnpCommand( - localPort: localPort, - remoteUsername: remoteUsername, - host: 'localhost', - privateKeyFileName: identityKeyPair?.privateKeyFileName, - localSshOptions: - (params.addForwardsToTunnel) ? null : params.localSshOptions, - connectionBean: sshrvResult, - ); - } -} diff --git a/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse.dart b/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse.dart deleted file mode 100644 index a03562b90..000000000 --- a/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse.dart +++ /dev/null @@ -1,79 +0,0 @@ -import 'dart:async'; - -import 'package:noports_core/src/sshnp/sshnp_core.dart'; -import 'package:noports_core/src/sshnp/brn/sshnp_ssh_key_handler.dart'; -import 'package:noports_core/sshnp.dart'; -import 'package:noports_core/sshrv.dart'; -import 'package:noports_core/utils.dart'; - -abstract class SSHNPReverse extends SshnpCore with SshnpLocalSSHKeyHandler { - SSHNPReverse({ - required super.atClient, - required super.params, - SshrvGenerator? sshrvGenerator, - super.shouldInitialize, - }) : sshrvGenerator = sshrvGenerator ?? DefaultArgs.sshrvGenerator; - - /// Function used to generate a [SSHRV] instance ([SSHRV.localbinary] by default) - final SshrvGenerator sshrvGenerator; - - /// Set by [generateEphemeralSshKeys] during [initialize], if we're not doing direct ssh. - /// sshnp generates a new keypair for each ssh session, using the algorithm specified - /// in [params.sshAlgorithm]. - /// sshnp will write [ephemeralKeyPair] to ~/.ssh/ephemeral_$sessionId - /// sshnp will write [ephemeralKeyPair.publicKey] to ~/.ssh/authorized_keys - /// sshnp will send the [ephemeralKeyPair.privateKey] to sshnpd - late final AtSshKeyPair ephemeralKeyPair; - - /// Local username, set by [initialize] - late final String localUsername; - - @override - Future initialize() async { - logger.info('Initializing SSHNPReverse'); - await super.initialize(); - if (!isSafeToInitialize) return; - - localUsername = getUserName(throwIfNull: true)!; - - logger.info('Generating ephemeral keypair'); - try { - ephemeralKeyPair = await keyUtil.generateKeyPair( - algorithm: params.sshAlgorithm, - identifier: 'ephemeral_$sessionId', - directory: keyUtil.sshnpHomeDirectory, - ); - } catch (e, s) { - logger.info('Failed to generate ephemeral keypair'); - throw SshnpError( - 'Failed to generate ephemeral keypair', - error: e, - stackTrace: s, - ); - } - - try { - logger.info('Adding ephemeral key to authorized_keys'); - await keyUtil.authorizePublicKey( - sshPublicKey: ephemeralKeyPair.publicKeyContents, - localSshdPort: params.localSshdPort, - sessionId: sessionId, - ); - } catch (e, s) { - throw SshnpError( - 'Failed to add ephemeral key to authorized_keys', - error: e, - stackTrace: s, - ); - } - } - - @override - Future cleanUp() async { - logger.info('Tidying up files'); -// Delete the generated RSA keys and remove the entry from ~/.ssh/authorized_keys - await keyUtil.deleteKeyPair(identifier: ephemeralKeyPair.identifier); - await keyUtil.deauthorizePublicKey(sessionId); - await super.cleanUp(); - } -} diff --git a/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse_impl.dart b/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse_impl.dart deleted file mode 100644 index edfd7271f..000000000 --- a/packages/noports_core/lib/src/sshnp/reverse_direction/sshnp_reverse_impl.dart +++ /dev/null @@ -1,92 +0,0 @@ -import 'dart:async'; - -import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/common/validation_utils.dart'; -import 'package:noports_core/src/sshnp/mixins/sshnpd_payload_handler.dart'; -import 'package:noports_core/src/sshnp/reverse_direction/sshnp_reverse.dart'; -import 'package:noports_core/sshnp.dart'; -import 'package:noports_core/sshrv.dart'; - -class SSHNPReverseImpl extends SSHNPReverse with SSHNPDDefaultPayloadHandler { - SSHNPReverseImpl({ - required AtClient atClient, - required SshnpParams params, - SshrvGenerator? sshrvGenerator, - bool? shouldInitialize, - }) : super( - atClient: atClient, - params: params, - sshrvGenerator: sshrvGenerator, - shouldInitialize: shouldInitialize, - ); - - @override - Future init() async { - logger.info('Initializing SSHNPReverseImpl'); - await super.init(); - completeInitialization(); - } - - @override - Future run() async { - await startAndWaitForInit(); - - logger.info('Requesting daemon to start reverse ssh session'); - - Future? sshrvResult; - if (usingSshrv) { - // Connect to rendezvous point using background process. - // sshnp (this program) can then exit without issue. - SSHRV sshrv = sshrvGenerator(host, sshrvdPort!, - localSshdPort: params.localSshdPort); - sshrvResult = sshrv.run(); - } - // send request to the daemon via notification - await notify( - AtKey() - ..key = 'ssh_request' - ..namespace = this.namespace - ..sharedBy = clientAtSign - ..sharedWith = sshnpdAtSign - ..metadata = (Metadata()..ttl = 10000), - signAndWrapAndJsonEncode( - atClient, - { - 'direct': false, - 'sessionId': sessionId, - 'host': host, - 'port': port, - 'username': localUsername, - 'remoteForwardPort': localPort, - 'privateKey': ephemeralKeyPair.privateKeyContents, - }, - ), - ); - - bool acked = await waitForDaemonResponse(); - if (!acked) { - var error = - SshnpError('sshnp connection timeout: waiting for daemon response'); - doneCompleter.completeError(error); - return error; - } - - if (sshnpdAckErrors) { - var error = - SshnpError('sshnp failed: with sshnpd acknowledgement errors'); - doneCompleter.completeError(error); - return error; - } - - doneCompleter.complete(); - return SshnpCommand( - localPort: localPort, - remoteUsername: remoteUsername, - host: 'localhost', - privateKeyFileName: identityKeyPair?.privateKeyFileName, - localSshOptions: - (params.addForwardsToTunnel) ? null : params.localSshOptions, - connectionBean: sshrvResult, - ); - } -} diff --git a/packages/noports_core/lib/src/sshnp/sshnp.dart b/packages/noports_core/lib/src/sshnp/sshnp.dart index 7fb098596..a8f9efbc2 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp.dart +++ b/packages/noports_core/lib/src/sshnp/sshnp.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'package:at_client/at_client.dart' hide StringBuffer; -import 'package:noports_core/src/sshnp/sshnp_device_list.dart'; -import 'package:noports_core/src/sshnp/sshnp_params/sshnp_params.dart'; -import 'package:noports_core/src/sshnp/sshnp_result.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_device_list.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_params.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_result.dart'; typedef AtClientGenerator = FutureOr Function(SshnpParams params); diff --git a/packages/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/noports_core/lib/src/sshnp/sshnp_core.dart index 08d6f7296..beaf13ab0 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/noports_core/lib/src/sshnp/sshnp_core.dart @@ -7,53 +7,58 @@ import 'package:at_client/at_client.dart' hide StringBuffer; import 'package:at_utils/at_logger.dart'; import 'package:logging/logging.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/src/common/async_completion.dart'; -import 'package:noports_core/src/common/async_initialization.dart'; -import 'package:noports_core/src/common/at_client_bindings.dart'; -import 'package:noports_core/src/sshnp/channels/sshnpd/sshnpd_channel.dart'; -import 'package:noports_core/src/sshnp/channels/sshrvd/sshrvd_channel.dart'; -import 'package:noports_core/src/sshnp/sshnp_device_list.dart'; +import 'package:noports_core/src/common/mixins/async_completion.dart'; +import 'package:noports_core/src/common/mixins/async_initialization.dart'; +import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_device_list.dart'; import 'package:noports_core/sshnp.dart'; - -import 'package:noports_core/utils.dart'; import 'package:uuid/uuid.dart'; -export 'forward_direction/sshnp_forward.dart'; -export 'forward_direction/sshnp_forward_dart.dart'; - -export 'reverse_direction/sshnp_reverse.dart'; -export 'reverse_direction/sshnp_reverse_impl.dart'; -export 'reverse_direction/sshnp_legacy_impl.dart'; - // If you've never seen an abstract implementation before, here it is :P @protected abstract class SshnpCore - with AsyncInitialization, AsyncDisposal, AtClientBindings + with AsyncInitialization, AsyncDisposal, AtClientBindings, SshnpKeyHandler implements Sshnp { // * AtClientBindings members + /// The logger for this class @override - final AtSignLogger logger = AtSignLogger(' sshnp '); + final AtSignLogger logger = AtSignLogger(' SshnpCore '); + + /// The [AtClient] to use for this instance @override final AtClient atClient; // * Main Parameters + + /// The parameters supplied for this instance @override final SshnpParams params; + + /// The session ID for this instance (UUID v4) final String sessionId; + + /// The namespace for this instance ('[params.device].sshnp') final String namespace; // * Volatile State + /// The local port to use for the initial tunnel's sshd forwarding + /// If this is 0, then a spare port will be found and set int localPort; - AtSshKeyPair? identityKeyPair; - // * Auxiliary classes - @protected - AtSSHKeyUtil get keyUtil; + /// The remote username to use for the ssh session + String? remoteUsername; + // * Communication Channels + + /// The channel to communicate with the sshrvd (host) @protected - SshrvdChannel get sshrvdChannel; + SshrvdChannel? get sshrvdChannel; + /// The channel to communicate with the sshnpd (daemon) @protected SshnpdChannel get sshnpdChannel; @@ -84,19 +89,23 @@ abstract class SshnpCore if (!isSafeToInitialize) return; logger.info('Initializing SSHNPCore'); - try { - if (!(await atSignIsActivated(atClient, params.sshnpdAtSign))) { - logger - .severe('Device address ${params.sshnpdAtSign} is not activated.'); - throw ('Device address ${params.sshnpdAtSign} is not activated.'); - } - } catch (e, s) { - throw SshnpError(e, stackTrace: s); - } + /// Start the sshnpd payload handler + await sshnpdChannel.callInitialization(); + + /// Set the remote username to use for the ssh session + remoteUsername = await sshnpdChannel.resolveRemoteUsername(); - // Start listening for response notifications from sshnpd - logger.info('Subscribing to notifications on $sessionId.$namespace@'); + /// Find a spare local port if required + await _findLocalPortIfRequired(); + /// Shares the public key if required + await sshnpdChannel.sharePublicKeyIfRequired(identityKeyPair); + + /// Retrieve the sshrvd host and port pair + await sshrvdChannel?.callInitialization(); + } + + Future _findLocalPortIfRequired() async { // TODO investigate if this is a problem on mobile // find a spare local port if (localPort == 0) { @@ -113,30 +122,6 @@ abstract class SshnpCore error: e, stackTrace: s); } } - - // await sharePublicKeyWithSshnpdIfRequired().catchError((e, s) { - // throw SSHNPError( - // 'Unable to share ssh public key with sshnpd', - // error: e, - // stackTrace: s, - // ); - // }); - - // If host has an @ then contact the sshrvd service for some ports - // if (host.startsWith('@')) { - // logger.info('Host is an atSign, fetching host and port from sshrvd'); - // await getHostAndPortFromSshrvd().catchError((e, s) { - // throw SSHNPError( - // 'Unable to get host and port from sshrvd', - // error: e, - // stackTrace: s, - // ); - // }); - // } - - logger.finer('Base initialization complete'); - // N.B. Don't complete initialization here, subclasses will do that - // This is in case they need to implement further initialization steps } @override diff --git a/packages/noports_core/lib/src/sshnp/forward_direction/sshnp_forward_dart.dart b/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler.dart similarity index 54% rename from packages/noports_core/lib/src/sshnp/forward_direction/sshnp_forward_dart.dart rename to packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler.dart index 55cd03726..36dc01810 100644 --- a/packages/noports_core/lib/src/sshnp/forward_direction/sshnp_forward_dart.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler.dart @@ -1,88 +1,127 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:io'; -import 'package:at_client/at_client.dart'; import 'package:dartssh2/dartssh2.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/src/sshnp/forward_direction/sshnp_forward.dart'; -import 'package:noports_core/src/sshnp/mixins/sshnpd_payload_handler.dart'; +import 'package:noports_core/src/sshnp/sshnp_core.dart'; import 'package:noports_core/sshnp.dart'; +import 'package:noports_core/utils.dart'; -abstract class SSHNPForwardDart extends SSHNPForward - with SSHNPDDefaultPayloadHandler { - SSHNPForwardDart({ - required AtClient atClient, - required SshnpParams params, - bool? shouldInitialize, - }) : super( - atClient: atClient, - params: params, - shouldInitialize: shouldInitialize, - ); +mixin SshnpInitialTunnelHandler { + @protected + Future startInitialTunnel({required String identifier}); +} + +mixin SshnpExecInitialTunnelHandler on SshnpCore + implements SshnpInitialTunnelHandler { + @override + Future startInitialTunnel({required String identifier}) async { + Process? process; + // If we are starting an initial tunnel, it should be to sshrvd, + // so it is safe to assume that sshrvdChannel is not null here + String argsString = '$remoteUsername@${sshrvdChannel?.host ?? params.host}' + ' -p ${sshrvdChannel!.port}' + ' -i $identifier' + ' -L $localPort:localhost:${params.remoteSshdPort}' + ' -o LogLevel=VERBOSE' + ' -t -t' + ' -o StrictHostKeyChecking=accept-new' + ' -o IdentitiesOnly=yes' + ' -o BatchMode=yes' + ' -o ExitOnForwardFailure=yes' + ' -f' // fork after authentication - this is important + ; + if (params.addForwardsToTunnel) { + argsString += ' ${params.localSshOptions.join(' ')}'; + } + argsString += ' sleep 15'; + + List args = argsString.split(' '); + + logger.info('$sessionId | Executing /usr/bin/ssh ${args.join(' ')}'); + // Because of the options we are using, we can wait for this process + // to complete, because it will exit with exitCode 0 once it has connected + // successfully + final soutBuf = StringBuffer(); + final serrBuf = StringBuffer(); + try { + process = await Process.start('/usr/bin/ssh', args); + process.stdout.transform(Utf8Decoder()).listen((String s) { + soutBuf.write(s); + logger.info('$sessionId | sshStdOut | $s'); + }, onError: (e) {}); + process.stderr.transform(Utf8Decoder()).listen((String s) { + serrBuf.write(s); + logger.info('$sessionId | sshStdErr | $s'); + }, onError: (e) {}); + await process.exitCode.timeout(Duration(seconds: 10)); + } on TimeoutException catch (e) { + throw SshnpError( + 'ssh process timed out after 10 seconds', + error: e, + ); + } + return process; + } +} + +mixin SshnpDartInitialTunnelHandler on SshnpCore + implements SshnpInitialTunnelHandler { /// Set up timer to check to see if all connections are down - @protected + @visibleForTesting String get terminateMessage => 'ssh session will terminate after ${params.idleTimeout} seconds' ' if it is not being used'; - @protected - Future startInitialTunnel() async { - await callInitialization(); - - var error = await requestSocketTunnelFromDaemon(); - if (error != null) { - throw error; - } - + @override + Future startInitialTunnel({required String identifier}) async { + // If we are starting an initial tunnel, it should be to sshrvd, + // so it is safe to assume that sshrvdChannel is not null here logger.info( - 'Starting direct ssh session to $host on port $sshrvdPort with forwardLocal of $localPort'); + 'Starting direct ssh session to ${sshrvdChannel!.host} on port ${sshrvdChannel!.port} with forwardLocal of $localPort'); try { late final SSHClient client; late final SSHSocket socket; try { - socket = await SSHSocket.connect(host, sshrvdPort); + socket = + await SSHSocket.connect(sshrvdChannel!.host, sshrvdChannel!.port) + .catchError((e) => throw e); } catch (e, s) { var error = SshnpError( - 'Failed to open socket to $host:$port : $e', + 'Failed to open socket to ${sshrvdChannel!.host}:${sshrvdChannel!.port} : $e', error: e, stackTrace: s, ); - doneCompleter.completeError(error); throw error; } try { + AtSshKeyPair keyPair = await keyUtil.getKeyPair(identifier: identifier); client = SSHClient( socket, - username: remoteUsername, - identities: [ - // A single private key file may contain multiple keys. - ...SSHKeyPair.fromPem(ephemeralPrivateKey) - ], + username: remoteUsername ?? getUserName(throwIfNull: true)!, + identities: [keyPair.keyPair], keepAliveInterval: Duration(seconds: 15), ); } catch (e, s) { - var error = SshnpError( - 'Failed to create SSHClient for ${params.remoteUsername}@$host:$port : $e', + throw SshnpError( + 'Failed to create SSHClient for ${params.remoteUsername}@${sshrvdChannel!.host}:${sshrvdChannel!.port} : $e', error: e, stackTrace: s, ); - doneCompleter.completeError(error); - throw error; } try { - await client.authenticated; + await client.authenticated.catchError((e) => throw e); } catch (e, s) { - var error = SshnpError( - 'Failed to authenticate as ${params.remoteUsername}@$host:$port : $e', + throw SshnpError( + 'Failed to authenticate as ${params.remoteUsername}@${sshrvdChannel!.host}:${sshrvdChannel!.port} : $e', error: e, stackTrace: s, ); - doneCompleter.completeError(error); - throw error; } int counter = 0; @@ -118,9 +157,10 @@ abstract class SSHNPForwardDart extends SSHNPForward // Start local forwarding to the remote sshd await startForwarding( - fLocalPort: localPort, - fRemoteHost: 'localhost', - fRemotePort: params.remoteSshdPort); + fLocalPort: localPort, + fRemoteHost: 'localhost', + fRemotePort: params.remoteSshdPort, + ); if (params.addForwardsToTunnel) { var optionsSplitBySpace = params.localSshOptions.join(' ').split(' '); @@ -169,17 +209,14 @@ abstract class SSHNPForwardDart extends SSHNPForward if (counter == 0 || client.isClosed) { timer.cancel(); if (!client.isClosed) client.close(); - doneCompleter.complete(); logger.shout( '$sessionId | no active connections - ssh session complete'); } }); return client; - } on SshnpError catch (e, s) { - doneCompleter.completeError(e, s); + } on SshnpError catch (_) { rethrow; } catch (e, s) { - doneCompleter.completeError(e, s); throw SshnpError( 'SSH Client failure : $e', error: e, diff --git a/packages/noports_core/lib/src/sshnp/util/sshnp_ssh_key_handler.dart b/packages/noports_core/lib/src/sshnp/util/sshnp_ssh_key_handler.dart new file mode 100644 index 000000000..a64619aa0 --- /dev/null +++ b/packages/noports_core/lib/src/sshnp/util/sshnp_ssh_key_handler.dart @@ -0,0 +1,56 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:meta/meta.dart'; +import 'package:noports_core/src/sshnp/sshnp_core.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_result.dart'; +import 'package:noports_core/utils.dart'; + +mixin SshnpKeyHandler { + @protected + AtSshKeyUtil get keyUtil; + + @protected + AtSshKeyPair? get identityKeyPair; +} + +mixin SshnpLocalSshKeyHandler on SshnpCore implements SshnpKeyHandler { + @override + LocalSshKeyUtil get keyUtil => _sshKeyUtil; + final LocalSshKeyUtil _sshKeyUtil = LocalSshKeyUtil(); + + @override + AtSshKeyPair? get identityKeyPair => _identityKeyPair; + AtSshKeyPair? _identityKeyPair; + + @override + Future initialize() async { + if (!isSafeToInitialize) return; + logger.info('Initializing SSHNPLocalSSHKeyHandler'); + + if (!keyUtil.isValidPlatform) { + throw SshnpError( + 'The current platform is not supported with the local SSH key handler: ${Platform.operatingSystem}'); + } + + if (params.identityFile != null) { + logger.info('Loading identity key pair from ${params.identityFile}'); + _identityKeyPair = await keyUtil.getKeyPair( + identifier: params.identityFile!, + passphrase: params.identityPassphrase, + ); + } + + await super.initialize(); + } +} + +mixin SshnpDartSshKeyHandler on SshnpCore implements SshnpKeyHandler { + @override + DartSshKeyUtil get keyUtil => _sshKeyUtil; + final DartSshKeyUtil _sshKeyUtil = DartSshKeyUtil(); + + @override + AtSshKeyPair? get identityKeyPair => _identityKeyPair; + AtSshKeyPair? _identityKeyPair; +} diff --git a/packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart similarity index 97% rename from packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_channel.dart rename to packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index 212382537..542054170 100644 --- a/packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -5,9 +5,9 @@ import 'package:at_client/at_client.dart'; import 'package:at_commons/at_builders.dart'; import 'package:at_utils/at_logger.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/src/common/async_initialization.dart'; -import 'package:noports_core/src/common/at_client_bindings.dart'; -import 'package:noports_core/src/sshnp/sshnp_device_list.dart'; +import 'package:noports_core/src/common/mixins/async_initialization.dart'; +import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; +import 'package:noports_core/src/sshnp/models/sshnp_device_list.dart'; import 'package:noports_core/sshnp.dart'; import 'package:noports_core/utils.dart'; diff --git a/packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_default_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart similarity index 87% rename from packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_default_channel.dart rename to packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart index e4828df31..97098d102 100644 --- a/packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_default_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart @@ -3,8 +3,8 @@ import 'dart:convert'; import 'package:at_client/at_client.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/src/sshnp/brn/sshnp_ssh_key_handler.dart'; -import 'package:noports_core/src/sshnp/channels/sshnpd/sshnpd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshnp_ssh_key_handler.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart'; import 'package:noports_core/utils.dart'; class SshnpdDefaultChannel extends SshnpdChannel @@ -17,12 +17,11 @@ class SshnpdDefaultChannel extends SshnpdChannel }); } -abstract mixin class SshnpdDefaultPayloadHandler implements SshnpdChannel { - @protected +mixin SshnpdDefaultPayloadHandler on SshnpdChannel { late final String ephemeralPrivateKey; @protected - bool get useLocalFileStorage => (this is SshnpLocalSSHKeyHandler); + bool get useLocalFileStorage => (this is SshnpLocalSshKeyHandler); @override Future handleSshnpdPayload(AtNotification notification) async { diff --git a/packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_version_3_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_unsigned_channel.dart similarity index 54% rename from packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_version_3_channel.dart rename to packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_unsigned_channel.dart index b55181473..4cf6082d2 100644 --- a/packages/noports_core/lib/src/sshnp/channels/sshnpd/sshnpd_version_3_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_unsigned_channel.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/sshnp/channels/sshnpd/sshnpd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart'; -class SshnpdVersion3Channel extends SshnpdChannel - with SshnpdVersion3PayloadHandler { - SshnpdVersion3Channel({ +class SshnpdUnsignedChannel extends SshnpdChannel + with SshnpdUnsignedPayloadHandler { + SshnpdUnsignedChannel({ required super.atClient, required super.params, required super.sessionId, @@ -13,7 +13,7 @@ class SshnpdVersion3Channel extends SshnpdChannel }); } -abstract mixin class SshnpdVersion3PayloadHandler implements SshnpdChannel { +mixin SshnpdUnsignedPayloadHandler on SshnpdChannel { @override Future handleSshnpdPayload(AtNotification notification) async { return (notification.value == 'connected'); diff --git a/packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart similarity index 91% rename from packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_channel.dart rename to packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart index a2aff0a3e..8e1099fae 100644 --- a/packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart @@ -3,8 +3,8 @@ import 'dart:async'; import 'package:at_client/at_client.dart'; import 'package:at_utils/at_utils.dart'; import 'package:meta/meta.dart'; -import 'package:noports_core/src/common/async_initialization.dart'; -import 'package:noports_core/src/common/at_client_bindings.dart'; +import 'package:noports_core/src/common/mixins/async_initialization.dart'; +import 'package:noports_core/src/common/mixins/at_client_bindings.dart'; import 'package:noports_core/sshnp.dart'; import 'package:noports_core/sshrv.dart'; import 'package:noports_core/sshrvd.dart'; @@ -57,17 +57,18 @@ abstract class SshrvdChannel with AsyncInitialization, AtClientBindings { required this.sshrvGenerator, }); - bool get usingSshrv => params.host.startsWith('@'); - @override Future initialize() async { - await getHostAndPortFromSshrvd(); + if (params.host.startsWith('@')) { + await getHostAndPortFromSshrvd(); + } else { + _host = params.host; + _port = params.port; + } completeInitialization(); } - Future run() async { - if (!usingSshrv) return null; - + Future runSshrv() async { await callInitialization(); if (_sshrvdPort == null) throw Exception('sshrvdPort is null'); diff --git a/packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_dart_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart similarity index 75% rename from packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_dart_channel.dart rename to packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart index 95cdd015c..1116d621b 100644 --- a/packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_dart_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart @@ -1,4 +1,4 @@ -import 'package:noports_core/src/sshnp/channels/sshrvd/sshrvd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; import 'package:noports_core/sshrv.dart'; class SshrvdDartChannel extends SshrvdChannel { diff --git a/packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_exec_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart similarity index 75% rename from packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_exec_channel.dart rename to packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart index 3b3eb5254..75c8579a4 100644 --- a/packages/noports_core/lib/src/sshnp/channels/sshrvd/sshrvd_exec_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart @@ -1,4 +1,4 @@ -import 'package:noports_core/src/sshnp/channels/sshrvd/sshrvd_channel.dart'; +import 'package:noports_core/src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; import 'package:noports_core/sshrv.dart'; class SshrvdExecChannel extends SshrvdChannel { diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd.dart b/packages/noports_core/lib/src/sshnpd/sshnpd.dart index 40247cad1..2ee04a071 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd.dart @@ -65,9 +65,9 @@ abstract class SSHNPD { /// The algorithm to use for ssh encryption /// Can be one of [SupportedSSHAlgorithm.values]: - /// - [SupportedSSHAlgorithm.ed25519] - /// - [SupportedSSHAlgorithm.rsa] - abstract final SupportedSSHAlgorithm sshAlgorithm; + /// - [SupportedSshAlgorithm.ed25519] + /// - [SupportedSshAlgorithm.rsa] + abstract final SupportedSshAlgorithm sshAlgorithm; static Future fromCommandLineArgs(List args, {AtClient? atClient, diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart index d3e1fb64a..7abad870a 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_impl.dart @@ -52,7 +52,7 @@ class SSHNPDImpl implements SSHNPD { final String ephemeralPermissions; @override - final SupportedSSHAlgorithm sshAlgorithm; + final SupportedSshAlgorithm sshAlgorithm; @override @visibleForTesting diff --git a/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart b/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart index 896fe24a9..ae3559f7a 100644 --- a/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart +++ b/packages/noports_core/lib/src/sshnpd/sshnpd_params.dart @@ -18,7 +18,7 @@ class SSHNPDParams { final String rootDomain; final int localSshdPort; final String ephemeralPermissions; - final SupportedSSHAlgorithm sshAlgorithm; + final SupportedSshAlgorithm sshAlgorithm; // Non param variables static final ArgParser parser = _createArgParser(); @@ -75,7 +75,7 @@ class SSHNPDParams { localSshdPort: int.tryParse(r['local-sshd-port']) ?? DefaultArgs.localSshdPort, ephemeralPermissions: r['ephemeral-permissions'], - sshAlgorithm: SupportedSSHAlgorithm.fromString(r['ssh-algorithm']), + sshAlgorithm: SupportedSshAlgorithm.fromString(r['ssh-algorithm']), ); } @@ -168,7 +168,7 @@ class SSHNPDParams { 'ssh-algorithm', defaultsTo: DefaultArgs.sshAlgorithm.toString(), help: 'Use RSA 4096 keys rather than the default ED25519 keys', - allowed: SupportedSSHAlgorithm.values.map((c) => c.toString()).toList(), + allowed: SupportedSshAlgorithm.values.map((c) => c.toString()).toList(), ); return parser; diff --git a/packages/noports_core/lib/sshnp.dart b/packages/noports_core/lib/sshnp.dart index dd18bc06e..7fe11dec1 100644 --- a/packages/noports_core/lib/sshnp.dart +++ b/packages/noports_core/lib/sshnp.dart @@ -1,6 +1,7 @@ library noports_core_sshnp; export 'src/sshnp/sshnp.dart'; -export 'src/sshnp/sshnp_result.dart'; -export 'src/sshnp/sshnp_params/sshnp_params.dart'; +export 'src/sshnp/models/sshnp_result.dart'; +export 'src/sshnp/models/sshnp_params.dart'; +export 'src/sshnp/models/sshnp_device_list.dart'; export 'src/common/types.dart'; diff --git a/packages/noports_core/lib/sshnp_core.dart b/packages/noports_core/lib/sshnp_core.dart deleted file mode 100644 index 68a3d2d9a..000000000 --- a/packages/noports_core/lib/sshnp_core.dart +++ /dev/null @@ -1,3 +0,0 @@ -library noports_core_sshnp_core; - -export 'src/sshnp/sshnp_core.dart'; diff --git a/packages/noports_core/lib/sshnp_foundation.dart b/packages/noports_core/lib/sshnp_foundation.dart new file mode 100644 index 000000000..47dce7f90 --- /dev/null +++ b/packages/noports_core/lib/sshnp_foundation.dart @@ -0,0 +1,49 @@ +library noports_core_sshnp_foundation; + +/// Sshnp Foundation Library +/// This library is used to build custom Sshnp implementations +/// It is not intended to be used directly by end users +/// All classes and methods are exported here for convenience + +// Core +export 'src/sshnp/sshnp.dart'; +export 'src/sshnp/sshnp_core.dart'; + +// Models +export 'src/sshnp/models/sshnp_arg.dart'; +export 'src/sshnp/models/sshnp_params.dart'; +export 'src/sshnp/models/sshnp_result.dart'; +export 'src/sshnp/models/sshnp_device_list.dart'; + +// SSHNP Utils +export 'src/sshnp/util/sshnpd_channel/sshnpd_channel.dart'; +export 'src/sshnp/util/sshnpd_channel/sshnpd_default_channel.dart'; +export 'src/sshnp/util/sshnpd_channel/sshnpd_unsigned_channel.dart'; + +export 'src/sshnp/util/sshrvd_channel/sshrvd_channel.dart'; +export 'src/sshnp/util/sshrvd_channel/sshrvd_dart_channel.dart'; +export 'src/sshnp/util/sshrvd_channel/sshrvd_exec_channel.dart'; + +export 'src/sshnp/util/sshnp_initial_tunnel_handler.dart'; +export 'src/sshnp/util/sshnp_ssh_key_handler.dart'; + +// Impl +export 'src/sshnp/impl/sshnp_dart_local_impl.dart'; +export 'src/sshnp/impl/sshnp_dart_pure_impl.dart'; +export 'src/sshnp/impl/sshnp_exec_local_impl.dart'; +export 'src/sshnp/impl/sshnp_reverse_impl.dart'; +export 'src/sshnp/impl/sshnp_unsigned_impl.dart'; + +// Common +export 'src/common/at_ssh_key_util/at_ssh_key_util.dart'; +export 'src/common/at_ssh_key_util/dart_ssh_key_util.dart'; +export 'src/common/at_ssh_key_util/local_ssh_key_util.dart'; + +export 'src/common/mixins/async_completion.dart'; +export 'src/common/mixins/async_initialization.dart'; +export 'src/common/mixins/at_client_bindings.dart'; + +export 'src/common/default_args.dart'; +export 'src/common/file_system_utils.dart'; +export 'src/common/types.dart'; +export 'src/common/validation_utils.dart'; diff --git a/packages/noports_core/lib/sshnp_params.dart b/packages/noports_core/lib/sshnp_params.dart index ff17300ef..3191f8118 100644 --- a/packages/noports_core/lib/sshnp_params.dart +++ b/packages/noports_core/lib/sshnp_params.dart @@ -1,7 +1,7 @@ library noports_core_sshnp_params; -export 'src/sshnp/sshnp_params/config_file_repository.dart'; -export 'src/sshnp/sshnp_params/config_key_repository.dart'; -export 'src/sshnp/sshnp_params/sshnp_params.dart'; -export 'src/sshnp/sshnp_params/sshnp_arg.dart'; +export 'src/sshnp/models/config_file_repository.dart'; +export 'src/sshnp/models/config_key_repository.dart'; +export 'src/sshnp/models/sshnp_params.dart'; +export 'src/sshnp/models/sshnp_arg.dart'; export 'src/common/types.dart'; diff --git a/packages/noports_core/lib/utils.dart b/packages/noports_core/lib/utils.dart index bd2286ba0..7d406166d 100644 --- a/packages/noports_core/lib/utils.dart +++ b/packages/noports_core/lib/utils.dart @@ -1,7 +1,7 @@ library noports_core_utils; +export 'src/common/at_ssh_key_util/at_ssh_key_util.dart'; +export 'src/common/default_args.dart'; +export 'src/common/file_system_utils.dart'; export 'src/common/types.dart'; export 'src/common/validation_utils.dart'; -export 'src/common/file_system_utils.dart'; -export 'src/common/ssh_key_utils.dart'; -export 'src/common/default_args.dart'; diff --git a/packages/noports_core/test/sshnp/sshnp_core_test.dart b/packages/noports_core/test/sshnp/sshnp_core_test.dart index af637921b..97f6e5d1e 100644 --- a/packages/noports_core/test/sshnp/sshnp_core_test.dart +++ b/packages/noports_core/test/sshnp/sshnp_core_test.dart @@ -1,37 +1,8 @@ -import 'dart:async'; - import 'package:at_client/at_client.dart'; -import 'package:noports_core/src/common/ssh_key_utils.dart'; -import 'package:noports_core/src/sshnp/sshnp_result.dart'; -import 'package:noports_core/sshnp_core.dart'; import 'package:noports_core/sshnp_params.dart'; import 'package:test/test.dart'; import 'package:mocktail/mocktail.dart'; -class MySSHNPCore extends SshnpCore { - MySSHNPCore({ - required super.atClient, - required super.params, - shouldInitialize = false, - }); - - @override - FutureOr handleSshnpdPayload(AtNotification notification) { - // TODO: implement handleSshnpdPayload - throw UnimplementedError(); - } - - @override - // TODO: implement keyUtil - AtSSHKeyUtil get keyUtil => throw UnimplementedError(); - - @override - FutureOr run() { - // TODO: implement run - throw UnimplementedError(); - } -} - class MockAtClient extends Mock implements AtClient {} class MockSSHNPParams extends Mock implements SshnpParams {} @@ -55,7 +26,8 @@ void main() { when(() => params.device).thenReturn('mydevice'); when(() => atClient.setPreferences(any())).thenReturn(null); - final sshnpCore = MySSHNPCore(atClient: atClient, params: params); +// TODO write a new MYSSHNPCore class + // final sshnpCore = MySSHNPCore(atClient: atClient, params: params); verify(() => atClient.getPreferences()).called(1); verify(() => params.device).called(1); diff --git a/packages/noports_core/test/sshnp/sshnp_params/sshnp_params_test.dart b/packages/noports_core/test/sshnp/sshnp_params/sshnp_params_test.dart index 521dc1d5c..251a57e49 100644 --- a/packages/noports_core/test/sshnp/sshnp_params/sshnp_params_test.dart +++ b/packages/noports_core/test/sshnp/sshnp_params/sshnp_params_test.dart @@ -27,7 +27,7 @@ void main() { expect(params.addForwardsToTunnel, isA()); expect(params.atKeysFilePath, isA()); expect(params.sshClient, isA()); - expect(params.sshAlgorithm, isA()); + expect(params.sshAlgorithm, isA()); expect(params.profileName, isA()); expect(params.listDevices, isA()); expect(params.toConfigLines(), isA>()); @@ -173,8 +173,8 @@ void main() { clientAtSign: '', sshnpdAtSign: '', host: '', - sshAlgorithm: SupportedSSHAlgorithm.rsa); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + sshAlgorithm: SupportedSshAlgorithm.rsa); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); test('SSHNPParams.profileName test', () { final params = SshnpParams( @@ -198,27 +198,27 @@ void main() { expect(params.clientAtSign, equals('')); expect(params.sshnpdAtSign, equals('')); expect(params.host, equals('')); - expect(params.device, equals(DefaultSSHNPArgs.device)); - expect(params.port, equals(DefaultSSHNPArgs.port)); - expect(params.localPort, equals(DefaultSSHNPArgs.localPort)); + expect(params.device, equals(DefaultSshnpArgs.device)); + expect(params.port, equals(DefaultSshnpArgs.port)); + expect(params.localPort, equals(DefaultSshnpArgs.localPort)); expect(params.identityFile, isNull); expect(params.identityPassphrase, isNull); expect( - params.sendSshPublicKey, equals(DefaultSSHNPArgs.sendSshPublicKey)); + params.sendSshPublicKey, equals(DefaultSshnpArgs.sendSshPublicKey)); expect( - params.localSshOptions, equals(DefaultSSHNPArgs.localSshOptions)); + params.localSshOptions, equals(DefaultSshnpArgs.localSshOptions)); expect(params.verbose, equals(DefaultArgs.verbose)); expect(params.remoteUsername, isNull); expect(params.atKeysFilePath, isNull); expect(params.rootDomain, equals(DefaultArgs.rootDomain)); expect(params.localSshdPort, equals(DefaultArgs.localSshdPort)); - expect(params.legacyDaemon, equals(DefaultSSHNPArgs.legacyDaemon)); - expect(params.listDevices, equals(DefaultSSHNPArgs.listDevices)); + expect(params.legacyDaemon, equals(DefaultSshnpArgs.legacyDaemon)); + expect(params.listDevices, equals(DefaultSshnpArgs.listDevices)); expect(params.remoteSshdPort, equals(DefaultArgs.remoteSshdPort)); expect(params.idleTimeout, equals(DefaultArgs.idleTimeout)); expect(params.addForwardsToTunnel, equals(DefaultArgs.addForwardsToTunnel)); - expect(params.sshClient, equals(DefaultSSHNPArgs.sshClient)); + expect(params.sshClient, equals(DefaultSshnpArgs.sshClient)); expect(params.sshAlgorithm, equals(DefaultArgs.sshAlgorithm)); }); test('SSHNPParams.merge() test (overrides take priority)', () { @@ -245,7 +245,7 @@ void main() { addForwardsToTunnel: true, atKeysFilePath: '~/.atsign/@myAtsign_keys.atKeys', sshClient: SupportedSshClient.dart, - sshAlgorithm: SupportedSSHAlgorithm.rsa, + sshAlgorithm: SupportedSshAlgorithm.rsa, ), ); expect(params.clientAtSign, equals('@myClientAtSign')); @@ -270,7 +270,7 @@ void main() { expect( params.atKeysFilePath, equals('~/.atsign/@myAtsign_keys.atKeys')); expect(params.sshClient, equals(SupportedSshClient.dart)); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); test('SSHNPParams.merge() test (null coalesce values)', () { final params = @@ -279,27 +279,27 @@ void main() { expect(params.clientAtSign, equals('')); expect(params.sshnpdAtSign, equals('')); expect(params.host, equals('')); - expect(params.device, equals(DefaultSSHNPArgs.device)); - expect(params.port, equals(DefaultSSHNPArgs.port)); - expect(params.localPort, equals(DefaultSSHNPArgs.localPort)); + expect(params.device, equals(DefaultSshnpArgs.device)); + expect(params.port, equals(DefaultSshnpArgs.port)); + expect(params.localPort, equals(DefaultSshnpArgs.localPort)); expect(params.identityFile, isNull); expect(params.identityPassphrase, isNull); expect( - params.sendSshPublicKey, equals(DefaultSSHNPArgs.sendSshPublicKey)); + params.sendSshPublicKey, equals(DefaultSshnpArgs.sendSshPublicKey)); expect( - params.localSshOptions, equals(DefaultSSHNPArgs.localSshOptions)); + params.localSshOptions, equals(DefaultSshnpArgs.localSshOptions)); expect(params.verbose, equals(DefaultArgs.verbose)); expect(params.remoteUsername, isNull); expect(params.atKeysFilePath, isNull); expect(params.rootDomain, equals(DefaultArgs.rootDomain)); expect(params.localSshdPort, equals(DefaultArgs.localSshdPort)); - expect(params.legacyDaemon, equals(DefaultSSHNPArgs.legacyDaemon)); - expect(params.listDevices, equals(DefaultSSHNPArgs.listDevices)); + expect(params.legacyDaemon, equals(DefaultSshnpArgs.legacyDaemon)); + expect(params.listDevices, equals(DefaultSshnpArgs.listDevices)); expect(params.remoteSshdPort, equals(DefaultArgs.remoteSshdPort)); expect(params.idleTimeout, equals(DefaultArgs.idleTimeout)); expect(params.addForwardsToTunnel, equals(DefaultArgs.addForwardsToTunnel)); - expect(params.sshClient, equals(DefaultSSHNPArgs.sshClient)); + expect(params.sshClient, equals(DefaultSshnpArgs.sshClient)); expect(params.sshAlgorithm, equals(DefaultArgs.sshAlgorithm)); }); test('SSHNPParams.fromJson() test', () { @@ -325,7 +325,7 @@ void main() { '"${SshnpArg.addForwardsToTunnelArg.name}": true,' '"${SshnpArg.keyFileArg.name}": "~/.atsign/@myAtsign_keys.atKeys",' '"${SshnpArg.sshClientArg.name}": "${SupportedSshClient.dart.toString()}",' - '"${SshnpArg.sshAlgorithmArg.name}": "${SupportedSSHAlgorithm.rsa.toString()}"' + '"${SshnpArg.sshAlgorithmArg.name}": "${SupportedSshAlgorithm.rsa.toString()}"' '}'; final params = SshnpParams.fromJson(json); @@ -352,7 +352,7 @@ void main() { expect( params.atKeysFilePath, equals('~/.atsign/@myAtsign_keys.atKeys')); expect(params.sshClient, equals(SupportedSshClient.dart)); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); test('SSHNPParams.fromPartial() test', () { final partial = SshnpPartialParams( @@ -387,7 +387,7 @@ void main() { '${SshnpArg.addForwardsToTunnelArg.bashName} = true', '${SshnpArg.keyFileArg.bashName} = ~/.atsign/@myAtsign_keys.atKeys', '${SshnpArg.sshClientArg.bashName} = ${SupportedSshClient.dart.toString()}', - '${SshnpArg.sshAlgorithmArg.bashName} = ${SupportedSSHAlgorithm.rsa.toString()}', + '${SshnpArg.sshAlgorithmArg.bashName} = ${SupportedSshAlgorithm.rsa.toString()}', ]; final params = SshnpParams.fromConfigLines('myProfile', configLines); expect(params.profileName, equals('myProfile')); @@ -430,7 +430,7 @@ void main() { addForwardsToTunnel: true, atKeysFilePath: '~/.atsign/@myAtsign_keys.atKeys', sshClient: SupportedSshClient.dart, - sshAlgorithm: SupportedSSHAlgorithm.rsa, + sshAlgorithm: SupportedSshAlgorithm.rsa, ); final configLines = params.toConfigLines(); // Since exact formatting is in question, @@ -475,7 +475,7 @@ void main() { addForwardsToTunnel: true, atKeysFilePath: '~/.atsign/@myAtsign_keys.atKeys', sshClient: SupportedSshClient.dart, - sshAlgorithm: SupportedSSHAlgorithm.rsa, + sshAlgorithm: SupportedSshAlgorithm.rsa, ); final argMap = params.toArgMap(); expect(argMap[SshnpArg.fromArg.name], equals('@myClientAtSign')); @@ -503,7 +503,7 @@ void main() { expect(argMap[SshnpArg.sshClientArg.name], equals(SupportedSshClient.dart.toString())); expect(argMap[SshnpArg.sshAlgorithmArg.name], - equals(SupportedSSHAlgorithm.rsa.toString())); + equals(SupportedSshAlgorithm.rsa.toString())); }); test('SSHNPParams.toJson', () { final params = SshnpParams( @@ -526,7 +526,7 @@ void main() { addForwardsToTunnel: true, atKeysFilePath: '~/.atsign/@myAtsign_keys.atKeys', sshClient: SupportedSshClient.dart, - sshAlgorithm: SupportedSSHAlgorithm.rsa, + sshAlgorithm: SupportedSshAlgorithm.rsa, ); final json = params.toJson(); final parsedParams = SshnpParams.fromJson(json); @@ -551,7 +551,7 @@ void main() { expect(parsedParams.atKeysFilePath, equals('~/.atsign/@myAtsign_keys.atKeys')); expect(parsedParams.sshClient, equals(SupportedSshClient.dart)); - expect(parsedParams.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(parsedParams.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); }); // group('SSHNPParams functions') }); // group('SSHNPParams') @@ -580,7 +580,7 @@ void main() { expect(partialParams.addForwardsToTunnel, isA()); expect(partialParams.atKeysFilePath, isA()); expect(partialParams.sshClient, isA()); - expect(partialParams.sshAlgorithm, isA()); + expect(partialParams.sshAlgorithm, isA()); expect(partialParams.profileName, isA()); expect(partialParams.listDevices, isA()); }); @@ -672,8 +672,8 @@ void main() { }); test('SSHNPPartialParams.sshAlgorithm test', () { final params = - SshnpPartialParams(sshAlgorithm: SupportedSSHAlgorithm.rsa); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + SshnpPartialParams(sshAlgorithm: SupportedSshAlgorithm.rsa); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); test('SSHNPPartialParams.profileName test', () { final params = SshnpPartialParams(profileName: 'myProfile'); @@ -734,7 +734,7 @@ void main() { addForwardsToTunnel: true, atKeysFilePath: '~/.atsign/@myAtsign_keys.atKeys', sshClient: SupportedSshClient.dart, - sshAlgorithm: SupportedSSHAlgorithm.rsa, + sshAlgorithm: SupportedSshAlgorithm.rsa, ), ); expect(params.clientAtSign, equals('@myClientAtSign')); @@ -758,7 +758,7 @@ void main() { expect( params.atKeysFilePath, equals('~/.atsign/@myAtsign_keys.atKeys')); expect(params.sshClient, equals(SupportedSshClient.dart)); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); test('SSHNPPartialParams.merge() test (null coalesce values)', () { final params = SshnpPartialParams.merge( @@ -782,7 +782,7 @@ void main() { addForwardsToTunnel: true, atKeysFilePath: '~/.atsign/@myAtsign_keys.atKeys', sshClient: SupportedSshClient.dart, - sshAlgorithm: SupportedSSHAlgorithm.rsa, + sshAlgorithm: SupportedSshAlgorithm.rsa, ), SshnpPartialParams.empty(), ); @@ -807,7 +807,7 @@ void main() { expect( params.atKeysFilePath, equals('~/.atsign/@myAtsign_keys.atKeys')); expect(params.sshClient, equals(SupportedSshClient.dart)); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); // TODO write tests for SSHNPPartialParams.fromFile() test('SSHNPPartial.fromConfigLines() test', () { @@ -831,7 +831,7 @@ void main() { addForwardsToTunnel: true, atKeysFilePath: '~/.atsign/@myAtsign_keys.atKeys', sshClient: SupportedSshClient.dart, - sshAlgorithm: SupportedSSHAlgorithm.rsa, + sshAlgorithm: SupportedSshAlgorithm.rsa, ); final configLines = params.toConfigLines(); // Since exact formatting is in question, @@ -878,7 +878,7 @@ void main() { '"${SshnpArg.addForwardsToTunnelArg.name}": true,' '"${SshnpArg.keyFileArg.name}": "~/.atsign/@myAtsign_keys.atKeys",' '"${SshnpArg.sshClientArg.name}": "${SupportedSshClient.dart.toString()}",' - '"${SshnpArg.sshAlgorithmArg.name}": "${SupportedSSHAlgorithm.rsa.toString()}"' + '"${SshnpArg.sshAlgorithmArg.name}": "${SupportedSshAlgorithm.rsa.toString()}"' '}'; final params = SshnpPartialParams.fromJson(json); @@ -905,7 +905,7 @@ void main() { expect( params.atKeysFilePath, equals('~/.atsign/@myAtsign_keys.atKeys')); expect(params.sshClient, equals(SupportedSshClient.dart)); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); test('SSHNPPartialParams.fromArgMap() test', () { final params = SshnpPartialParams.fromArgMap({ @@ -930,7 +930,7 @@ void main() { SshnpArg.addForwardsToTunnelArg.name: true, SshnpArg.keyFileArg.name: '~/.atsign/@myAtsign_keys.atKeys', SshnpArg.sshClientArg.name: SupportedSshClient.dart.toString(), - SshnpArg.sshAlgorithmArg.name: SupportedSSHAlgorithm.rsa.toString(), + SshnpArg.sshAlgorithmArg.name: SupportedSshAlgorithm.rsa.toString(), }); expect(params.profileName, equals('myProfile')); expect(params.clientAtSign, equals('@myClientAtSign')); @@ -955,7 +955,7 @@ void main() { expect( params.atKeysFilePath, equals('~/.atsign/@myAtsign_keys.atKeys')); expect(params.sshClient, equals(SupportedSshClient.dart)); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); test('SSHNPPartialParams.fromArgList() test', () { final argList = [ @@ -1002,7 +1002,7 @@ void main() { '--${SshnpArg.sshClientArg.name}', SupportedSshClient.dart.toString(), '--${SshnpArg.sshAlgorithmArg.name}', - SupportedSSHAlgorithm.rsa.toString(), + SupportedSshAlgorithm.rsa.toString(), ]; final params = SshnpPartialParams.fromArgList(argList); expect(params.profileName, equals('myProfile')); @@ -1026,7 +1026,7 @@ void main() { expect(params.idleTimeout, equals(120)); expect(params.addForwardsToTunnel, equals(true)); expect(params.sshClient, equals(SupportedSshClient.dart)); - expect(params.sshAlgorithm, equals(SupportedSSHAlgorithm.rsa)); + expect(params.sshAlgorithm, equals(SupportedSshAlgorithm.rsa)); }); }); // group('SSHNPPartialParams factories') }); // group('SSHNPPartialParams') diff --git a/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_run_action.dart b/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_run_action.dart index 9b1772549..f0000a9dc 100644 --- a/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_run_action.dart +++ b/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_run_action.dart @@ -42,7 +42,7 @@ class _ProfileRunActionState extends ConsumerState { // TODO ensure that this keyPair gets uploaded to the app first AtClient atClient = AtClientManager.getInstance().atClient; - DartSSHKeyUtil keyUtil = DartSSHKeyUtil(); + DartSshKeyUtil keyUtil = DartSshKeyUtil(); AtSshKeyPair keyPair = await keyUtil.getKeyPair( identifier: params.identityFile ?? 'id_${atClient.getCurrentAtSign()!.replaceAll('@', '')}', diff --git a/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_terminal_action.dart b/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_terminal_action.dart index ab744d584..d652e6572 100644 --- a/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_terminal_action.dart +++ b/packages/sshnp_gui/lib/src/presentation/widgets/profile_actions/profile_terminal_action.dart @@ -41,7 +41,7 @@ class _ProfileTerminalActionState extends ConsumerState { // TODO ensure that this keyPair gets uploaded to the app first AtClient atClient = AtClientManager.getInstance().atClient; - DartSSHKeyUtil keyUtil = DartSSHKeyUtil(); + DartSshKeyUtil keyUtil = DartSshKeyUtil(); AtSshKeyPair keyPair = await keyUtil.getKeyPair( identifier: params.identityFile ?? 'id_${atClient.getCurrentAtSign()!.replaceAll('@', '')}',