diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart index 84c62cbfe..4226b7257 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart @@ -115,6 +115,7 @@ class SshnpArg { localSshOptionsArg, verboseArg, remoteUserNameArg, + tunnelUserNameArg, rootDomainArg, localSshdPortArg, legacyDaemonArg, @@ -278,6 +279,11 @@ class SshnpArg { abbr: 'u', help: 'username to use in the ssh session on the remote host', ); + static const tunnelUserNameArg = SshnpArg( + name: 'tunnel-user-name', + abbr: 'U', + help: 'username to use for the initial ssh tunnel', + ); static const rootDomainArg = SshnpArg( name: 'root-domain', help: 'atDirectory domain', diff --git a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart index 43006dc67..cfe3f81f8 100644 --- a/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart +++ b/packages/noports_core/lib/src/sshnp/models/sshnp_params.dart @@ -25,6 +25,7 @@ class SshnpParams { final bool sendSshPublicKey; final List localSshOptions; final String? remoteUsername; + final String? tunnelUsername; final bool verbose; final String rootDomain; final int localSshdPort; @@ -37,8 +38,9 @@ class SshnpParams { final SupportedSshAlgorithm sshAlgorithm; /// Special Arguments - final String? - profileName; // automatically populated with the filename if from a configFile + + /// automatically populated with the filename if from a configFile + final String? profileName; /// Operation flags final bool listDevices; @@ -57,6 +59,7 @@ class SshnpParams { this.localSshOptions = DefaultSshnpArgs.localSshOptions, this.verbose = DefaultArgs.verbose, this.remoteUsername, + this.tunnelUsername, this.atKeysFilePath, this.rootDomain = DefaultArgs.rootDomain, this.localSshdPort = DefaultArgs.localSshdPort, @@ -98,6 +101,7 @@ class SshnpParams { sendSshPublicKey: params2.sendSshPublicKey ?? params1.sendSshPublicKey, localSshOptions: params2.localSshOptions ?? params1.localSshOptions, remoteUsername: params2.remoteUsername ?? params1.remoteUsername, + tunnelUsername: params2.tunnelUsername ?? params1.tunnelUsername, verbose: params2.verbose ?? params1.verbose, rootDomain: params2.rootDomain ?? params1.rootDomain, localSshdPort: params2.localSshdPort ?? params1.localSshdPort, @@ -139,6 +143,7 @@ class SshnpParams { partial.localSshOptions ?? DefaultSshnpArgs.localSshOptions, verbose: partial.verbose ?? DefaultArgs.verbose, remoteUsername: partial.remoteUsername, + tunnelUsername: partial.tunnelUsername, atKeysFilePath: partial.atKeysFilePath, rootDomain: partial.rootDomain ?? DefaultArgs.rootDomain, localSshdPort: partial.localSshdPort ?? DefaultArgs.localSshdPort, @@ -190,6 +195,7 @@ class SshnpParams { SshnpArg.sendSshPublicKeyArg.name: sendSshPublicKey, SshnpArg.localSshOptionsArg.name: localSshOptions, SshnpArg.remoteUserNameArg.name: remoteUsername, + SshnpArg.tunnelUserNameArg.name: tunnelUsername, SshnpArg.verboseArg.name: verbose, SshnpArg.rootDomainArg.name: rootDomain, SshnpArg.localSshdPortArg.name: localSshdPort, @@ -229,6 +235,7 @@ class SshnpPartialParams { final bool? sendSshPublicKey; final List? localSshOptions; final String? remoteUsername; + final String? tunnelUsername; final bool? verbose; final String? rootDomain; final bool? legacyDaemon; @@ -255,6 +262,7 @@ class SshnpPartialParams { this.sendSshPublicKey, this.localSshOptions, this.remoteUsername, + this.tunnelUsername, this.verbose, this.rootDomain, this.localSshdPort, @@ -291,6 +299,7 @@ class SshnpPartialParams { sendSshPublicKey: params2.sendSshPublicKey ?? params1.sendSshPublicKey, localSshOptions: params2.localSshOptions ?? params1.localSshOptions, remoteUsername: params2.remoteUsername ?? params1.remoteUsername, + tunnelUsername: params2.tunnelUsername ?? params1.tunnelUsername, verbose: params2.verbose ?? params1.verbose, rootDomain: params2.rootDomain ?? params1.rootDomain, localSshdPort: params2.localSshdPort ?? params1.localSshdPort, @@ -339,6 +348,7 @@ class SshnpPartialParams { ? null : List.from(args[SshnpArg.localSshOptionsArg.name]), remoteUsername: args[SshnpArg.remoteUserNameArg.name], + tunnelUsername: args[SshnpArg.tunnelUserNameArg.name], verbose: args[SshnpArg.verboseArg.name], rootDomain: args[SshnpArg.rootDomainArg.name], localSshdPort: args[SshnpArg.localSshdPortArg.name], diff --git a/packages/noports_core/lib/src/sshnp/sshnp_core.dart b/packages/noports_core/lib/src/sshnp/sshnp_core.dart index 71ded03c6..0de4d3f25 100644 --- a/packages/noports_core/lib/src/sshnp/sshnp_core.dart +++ b/packages/noports_core/lib/src/sshnp/sshnp_core.dart @@ -46,6 +46,9 @@ abstract class SshnpCore /// The remote username to use for the ssh session String? remoteUsername; + /// The username to use for the initial ssh tunnel session + String? tunnelUsername; + // * Communication Channels /// The channel to communicate with the sshrvd (host) @@ -83,6 +86,10 @@ abstract class SshnpCore /// Set the remote username to use for the ssh session remoteUsername = await sshnpdChannel.resolveRemoteUsername(); + /// Set the username to use for the initial ssh tunnel + tunnelUsername = await sshnpdChannel.resolveTunnelUsername( + remoteUsername: remoteUsername); + /// Shares the public key if required await sshnpdChannel.sharePublicKeyIfRequired(identityKeyPair); diff --git a/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_dart_initial_tunnel_handler.dart b/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_dart_initial_tunnel_handler.dart index efb6c59f2..ec5fe5eff 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_dart_initial_tunnel_handler.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_dart_initial_tunnel_handler.dart @@ -39,17 +39,18 @@ mixin SshnpDartInitialTunnelHandler on SshnpCore throw error; } + var usernameForTunnel = tunnelUsername ?? getUserName(throwIfNull: true)!; try { AtSshKeyPair keyPair = await keyUtil.getKeyPair(identifier: identifier); client = SSHClient( socket, - username: remoteUsername ?? getUserName(throwIfNull: true)!, + username: usernameForTunnel, identities: [keyPair.keyPair], keepAliveInterval: Duration(seconds: 15), ); } catch (e, s) { throw SshnpError( - 'Failed to create SSHClient for ${params.remoteUsername}@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e', + 'Failed to create SSHClient for $usernameForTunnel@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e', error: e, stackTrace: s, ); @@ -59,7 +60,7 @@ mixin SshnpDartInitialTunnelHandler on SshnpCore await client.authenticated.catchError((e) => throw e); } catch (e, s) { throw SshnpError( - 'Failed to authenticate as ${params.remoteUsername}@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e', + 'Failed to authenticate as $usernameForTunnel@${sshrvdChannel.host}:${sshrvdChannel.sshrvdPort} : $e', error: e, stackTrace: s, ); diff --git a/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_openssh_initial_tunnel_handler.dart b/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_openssh_initial_tunnel_handler.dart index 179a4e8e8..f2768f967 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_openssh_initial_tunnel_handler.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnp_initial_tunnel_handler/sshnp_openssh_initial_tunnel_handler.dart @@ -16,7 +16,7 @@ mixin SshnpOpensshInitialTunnelHandler on SshnpCore 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}' + String argsString = '$tunnelUsername@${sshrvdChannel.host}' ' -p ${sshrvdChannel.sshrvdPort}' ' -i $identifier' ' -L $localPort:localhost:${params.remoteSshdPort}' diff --git a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart index 058db128f..651528cf4 100644 --- a/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart +++ b/packages/noports_core/lib/src/sshnp/util/sshnpd_channel/sshnpd_channel.dart @@ -161,6 +161,18 @@ abstract class SshnpdChannel with AsyncInitialization, AtClientBindings { } } + /// Resolve the username to use in the initial ssh tunnel + /// If [params.tunnelUsername] is set, it will be used. + /// Otherwise, the username will be set to [remoteUsername] + Future resolveTunnelUsername( + {required String? remoteUsername}) async { + if (params.tunnelUsername != null) { + return params.tunnelUsername!; + } else { + return remoteUsername; + } + } + /// List all available devices from the daemon. /// Returns a [SSHPNPDeviceList] object which contains a map of device names /// and corresponding info, and a list of active devices (devices which also diff --git a/packages/noports_core/lib/sshrv.dart b/packages/noports_core/lib/sshrv.dart index 3a2b323b9..3cfa5b3a9 100644 --- a/packages/noports_core/lib/sshrv.dart +++ b/packages/noports_core/lib/sshrv.dart @@ -1,3 +1,3 @@ library noports_core_sshrv; -export 'src/sshrv/sshrv.dart'; \ No newline at end of file +export 'src/sshrv/sshrv.dart'; diff --git a/packages/noports_core/test/sshnp/models/config_file_repository_test.dart b/packages/noports_core/test/sshnp/models/config_file_repository_test.dart index 29a6bb237..92d5b8ac6 100644 --- a/packages/noports_core/test/sshnp/models/config_file_repository_test.dart +++ b/packages/noports_core/test/sshnp/models/config_file_repository_test.dart @@ -8,14 +8,23 @@ void main() { test('ConfigFileRepository.atKeyFromProfileName test', () async { String profileName = 'myProfileName'; - expect(ConfigFileRepository.getDefaultSshnpConfigDirectory(getHomeDirectory()!), isA()); - expect(ConfigFileRepository.fromProfileName(profileName), isA>()); + expect( + ConfigFileRepository.getDefaultSshnpConfigDirectory( + getHomeDirectory()!), + isA()); + expect(ConfigFileRepository.fromProfileName(profileName), + isA>()); expect(ConfigFileRepository.fromProfileName(profileName), completes); expect( - await ConfigFileRepository.fromProfileName(profileName, basenameOnly: false), - equals(path.join(getHomeDirectory()!, '.sshnp', 'config', '$profileName.env')), + await ConfigFileRepository.fromProfileName(profileName, + basenameOnly: false), + equals(path.join( + getHomeDirectory()!, '.sshnp', 'config', '$profileName.env')), ); - expect(await ConfigFileRepository.fromProfileName(profileName, basenameOnly: true), equals('$profileName.env')); + expect( + await ConfigFileRepository.fromProfileName(profileName, + basenameOnly: true), + equals('$profileName.env')); }); group('[depends on ConfigFileRepository.atKeyFromProfileName]', () { diff --git a/packages/noports_core/test/sshnp/models/sshnp_params_test.dart b/packages/noports_core/test/sshnp/models/sshnp_params_test.dart index 14d2023e4..6b43debf2 100644 --- a/packages/noports_core/test/sshnp/models/sshnp_params_test.dart +++ b/packages/noports_core/test/sshnp/models/sshnp_params_test.dart @@ -18,6 +18,7 @@ void main() { expect(params.sendSshPublicKey, isA()); expect(params.localSshOptions, isA>()); expect(params.remoteUsername, isA()); + expect(params.tunnelUsername, isA()); expect(params.verbose, isA()); expect(params.rootDomain, isA()); expect(params.localSshdPort, isA()); @@ -110,6 +111,14 @@ void main() { remoteUsername: 'myUsername'); expect(params.remoteUsername, equals('myUsername')); }); + test('SshnpParams.tunnelUsername test', () { + final params = SshnpParams( + clientAtSign: '', + sshnpdAtSign: '', + host: '', + tunnelUsername: 'myTunnelUsername'); + expect(params.tunnelUsername, equals('myTunnelUsername')); + }); test('SshnpParams.verbose test', () { final params = SshnpParams( clientAtSign: '', sshnpdAtSign: '', host: '', verbose: true); @@ -209,6 +218,7 @@ void main() { params.localSshOptions, equals(DefaultSshnpArgs.localSshOptions)); expect(params.verbose, equals(DefaultArgs.verbose)); expect(params.remoteUsername, isNull); + expect(params.tunnelUsername, isNull); expect(params.atKeysFilePath, isNull); expect(params.rootDomain, equals(DefaultArgs.rootDomain)); expect(params.localSshdPort, equals(DefaultArgs.localSshdPort)); @@ -236,6 +246,7 @@ void main() { sendSshPublicKey: true, localSshOptions: ['-L 127.0.01:8080:127.0.0.1:80'], remoteUsername: 'myUsername', + tunnelUsername: 'myTunnelUsername', verbose: true, rootDomain: 'root.atsign.wtf', localSshdPort: 4567, @@ -260,6 +271,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); @@ -290,6 +302,7 @@ void main() { params.localSshOptions, equals(DefaultSshnpArgs.localSshOptions)); expect(params.verbose, equals(DefaultArgs.verbose)); expect(params.remoteUsername, isNull); + expect(params.tunnelUsername, isNull); expect(params.atKeysFilePath, isNull); expect(params.rootDomain, equals(DefaultArgs.rootDomain)); expect(params.localSshdPort, equals(DefaultArgs.localSshdPort)); @@ -316,6 +329,7 @@ void main() { '"${SshnpArg.sendSshPublicKeyArg.name}": true,' '"${SshnpArg.localSshOptionsArg.name}": ["-L 127.0.01:8080:127.0.0.1:80"],' '"${SshnpArg.remoteUserNameArg.name}": "myUsername",' + '"${SshnpArg.tunnelUserNameArg.name}": "myTunnelUsername",' '"${SshnpArg.verboseArg.name}": true,' '"${SshnpArg.rootDomainArg.name}": "root.atsign.wtf",' '"${SshnpArg.localSshdPortArg.name}": 4567,' @@ -342,6 +356,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); @@ -378,6 +393,7 @@ void main() { '${SshnpArg.sendSshPublicKeyArg.bashName} = true', '${SshnpArg.localSshOptionsArg.bashName} = -L 127.0.01:8080:127.0.0.1:80', '${SshnpArg.remoteUserNameArg.bashName} = myUsername', + '${SshnpArg.tunnelUserNameArg.bashName} = myTunnelUsername', '${SshnpArg.verboseArg.bashName} = true', '${SshnpArg.rootDomainArg.bashName} = root.atsign.wtf', '${SshnpArg.localSshdPortArg.bashName} = 4567', @@ -401,6 +417,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); @@ -422,6 +439,7 @@ void main() { sendSshPublicKey: true, localSshOptions: ['-L 127.0.01:8080:127.0.0.1:80'], remoteUsername: 'myUsername', + tunnelUsername: 'myTunnelUsername', verbose: true, rootDomain: 'root.atsign.wtf', localSshdPort: 4567, @@ -449,6 +467,7 @@ void main() { expect(parsedParams.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(parsedParams.remoteUsername, equals('myUsername')); + expect(parsedParams.tunnelUsername, equals('myTunnelUsername')); expect(parsedParams.verbose, equals(true)); expect(parsedParams.rootDomain, equals('root.atsign.wtf')); expect(parsedParams.localSshdPort, equals(4567)); @@ -467,6 +486,7 @@ void main() { sendSshPublicKey: true, localSshOptions: ['-L 127.0.01:8080:127.0.0.1:80'], remoteUsername: 'myUsername', + tunnelUsername: 'myTunnelUsername', verbose: true, rootDomain: 'root.atsign.wtf', localSshdPort: 4567, @@ -492,6 +512,8 @@ void main() { expect(argMap[SshnpArg.localSshOptionsArg.name], equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(argMap[SshnpArg.remoteUserNameArg.name], equals('myUsername')); + expect(argMap[SshnpArg.tunnelUserNameArg.name], + equals('myTunnelUsername')); expect(argMap[SshnpArg.verboseArg.name], equals(true)); expect(argMap[SshnpArg.rootDomainArg.name], equals('root.atsign.wtf')); expect(argMap[SshnpArg.localSshdPortArg.name], equals(4567)); @@ -518,6 +540,7 @@ void main() { sendSshPublicKey: true, localSshOptions: ['-L 127.0.01:8080:127.0.0.1:80'], remoteUsername: 'myUsername', + tunnelUsername: 'myTunnelUsername', verbose: true, rootDomain: 'root.atsign.wtf', localSshdPort: 4567, @@ -542,6 +565,7 @@ void main() { expect(parsedParams.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(parsedParams.remoteUsername, equals('myUsername')); + expect(parsedParams.tunnelUsername, equals('myTunnelUsername')); expect(parsedParams.verbose, equals(true)); expect(parsedParams.rootDomain, equals('root.atsign.wtf')); expect(parsedParams.localSshdPort, equals(4567)); @@ -571,6 +595,7 @@ void main() { expect(partialParams.sendSshPublicKey, isA()); expect(partialParams.localSshOptions, isA?>()); expect(partialParams.remoteUsername, isA()); + expect(partialParams.tunnelUsername, isA()); expect(partialParams.verbose, isA()); expect(partialParams.rootDomain, isA()); expect(partialParams.localSshdPort, isA()); @@ -632,6 +657,10 @@ void main() { final params = SshnpPartialParams(remoteUsername: 'myUsername'); expect(params.remoteUsername, equals('myUsername')); }); + test('SshnpPartialParams.tunnelUsername test', () { + final params = SshnpPartialParams(tunnelUsername: 'myTunnelUsername'); + expect(params.tunnelUsername, equals('myTunnelUsername')); + }); test('SshnpPartialParams.verbose test', () { final params = SshnpPartialParams(verbose: true); expect(params.verbose, equals(true)); @@ -700,6 +729,7 @@ void main() { expect(params.localSshOptions, isNull); expect(params.verbose, isNull); expect(params.remoteUsername, isNull); + expect(params.tunnelUsername, isNull); expect(params.rootDomain, isNull); expect(params.localSshdPort, isNull); expect(params.legacyDaemon, isNull); @@ -726,6 +756,7 @@ void main() { sendSshPublicKey: true, localSshOptions: ['-L 127.0.01:8080:127.0.0.1:80'], remoteUsername: 'myUsername', + tunnelUsername: 'myTunnelUsername', verbose: true, rootDomain: 'root.atsign.wtf', localSshdPort: 4567, @@ -749,6 +780,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); @@ -774,6 +806,7 @@ void main() { sendSshPublicKey: true, localSshOptions: ['-L 127.0.01:8080:127.0.0.1:80'], remoteUsername: 'myUsername', + tunnelUsername: 'myTunnelUsername', verbose: true, rootDomain: 'root.atsign.wtf', localSshdPort: 4567, @@ -798,6 +831,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); @@ -823,6 +857,7 @@ void main() { sendSshPublicKey: true, localSshOptions: ['-L 127.0.01:8080:127.0.0.1:80'], remoteUsername: 'myUsername', + tunnelUsername: 'myTunnelUsername', verbose: true, rootDomain: 'root.atsign.wtf', localSshdPort: 4567, @@ -850,6 +885,7 @@ void main() { expect(parsedParams.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(parsedParams.remoteUsername, equals('myUsername')); + expect(parsedParams.tunnelUsername, equals('myTunnelUsername')); expect(parsedParams.verbose, equals(true)); expect(parsedParams.rootDomain, equals('root.atsign.wtf')); expect(parsedParams.localSshdPort, equals(4567)); @@ -869,6 +905,7 @@ void main() { '"${SshnpArg.sendSshPublicKeyArg.name}": true,' '"${SshnpArg.localSshOptionsArg.name}": ["-L 127.0.01:8080:127.0.0.1:80"],' '"${SshnpArg.remoteUserNameArg.name}": "myUsername",' + '"${SshnpArg.tunnelUserNameArg.name}": "myTunnelUsername",' '"${SshnpArg.verboseArg.name}": true,' '"${SshnpArg.rootDomainArg.name}": "root.atsign.wtf",' '"${SshnpArg.localSshdPortArg.name}": 4567,' @@ -895,6 +932,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); @@ -921,6 +959,7 @@ void main() { SshnpArg.sendSshPublicKeyArg.name: true, SshnpArg.localSshOptionsArg.name: ['-L 127.0.01:8080:127.0.0.1:80'], SshnpArg.remoteUserNameArg.name: 'myUsername', + SshnpArg.tunnelUserNameArg.name: 'myTunnelUsername', SshnpArg.verboseArg.name: true, SshnpArg.rootDomainArg.name: 'root.atsign.wtf', SshnpArg.localSshdPortArg.name: 4567, @@ -945,6 +984,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); @@ -983,6 +1023,8 @@ void main() { '-L 127.0.01:8080:127.0.0.1:80', '--${SshnpArg.remoteUserNameArg.name}', 'myUsername', + '--${SshnpArg.tunnelUserNameArg.name}', + 'myTunnelUsername', '--${SshnpArg.verboseArg.name}', 'true', '--${SshnpArg.rootDomainArg.name}', @@ -1018,6 +1060,7 @@ void main() { expect( params.localSshOptions, equals(['-L 127.0.01:8080:127.0.0.1:80'])); expect(params.remoteUsername, equals('myUsername')); + expect(params.tunnelUsername, equals('myTunnelUsername')); expect(params.verbose, equals(true)); expect(params.rootDomain, equals('root.atsign.wtf')); expect(params.localSshdPort, equals(4567)); diff --git a/packages/noports_core/test/sshnp/sshnp_core_test.dart b/packages/noports_core/test/sshnp/sshnp_core_test.dart index ca290106e..772052231 100644 --- a/packages/noports_core/test/sshnp/sshnp_core_test.dart +++ b/packages/noports_core/test/sshnp/sshnp_core_test.dart @@ -54,6 +54,9 @@ void main() { .thenAnswer((_) async {}); when(() => mockSshnpdChannel.resolveRemoteUsername()) .thenAnswer((_) async => 'myRemoteUsername'); + when(() => mockSshnpdChannel.resolveTunnelUsername( + remoteUsername: any(named: 'remoteUsername'))) + .thenAnswer((_) async => 'myTunnelUsername'); when(() => mockSshnpdChannel.sharePublicKeyIfRequired( identityKeyPair ?? any())).thenAnswer((_) async {}); when(() => mockSshrvdChannel.callInitialization()) @@ -129,6 +132,8 @@ void main() { () => stubbedInitialize(), () => mockSshnpdChannel.callInitialization(), () => mockSshnpdChannel.resolveRemoteUsername(), + () => mockSshnpdChannel.resolveTunnelUsername( + remoteUsername: 'myRemoteUsername'), () => mockSshnpdChannel .sharePublicKeyIfRequired(sshnpCore.identityKeyPair), () => mockSshrvdChannel.callInitialization(), @@ -140,6 +145,8 @@ void main() { verifyNever(() => stubbedInitialize()); verifyNever(() => mockSshnpdChannel.callInitialization()); verifyNever(() => mockSshnpdChannel.resolveRemoteUsername()); + verifyNever(() => mockSshnpdChannel.resolveTunnelUsername( + remoteUsername: 'myRemoteUsername')); verifyNever(() => mockSshnpdChannel .sharePublicKeyIfRequired(sshnpCore.identityKeyPair)); verifyNever(() => mockSshrvdChannel.callInitialization()); @@ -153,6 +160,41 @@ void main() { verifyNever(() => stubbedCompleteInitialization()); verifyNever(() => mockSshrvdChannel.callInitialization()); }); + test('tunnelUsername not supplied', () async { + final params = SshnpParams( + clientAtSign: '@client', + sshnpdAtSign: '@daemon', + host: 'foo.bar.test', + remoteUsername: 'alice'); + final channel = SshnpdDefaultChannel( + atClient: mockAtClient, + params: params, + sessionId: 'test_tunnelUsername_not_supplied', + namespace: 'test'); + final remoteUsername = await channel.resolveRemoteUsername(); + expect(remoteUsername, 'alice'); + expect( + await channel.resolveTunnelUsername(remoteUsername: remoteUsername), + 'alice'); + }); + test('tunnelUsername supplied', () async { + final params = SshnpParams( + clientAtSign: '@client', + sshnpdAtSign: '@daemon', + host: 'foo.bar.test', + remoteUsername: 'alice', + tunnelUsername: 'bob'); + final channel = SshnpdDefaultChannel( + atClient: mockAtClient, + params: params, + sessionId: 'test_tunnelUsername_supplied', + namespace: 'test'); + final remoteUsername = await channel.resolveRemoteUsername(); + expect(remoteUsername, 'alice'); + expect( + await channel.resolveTunnelUsername(remoteUsername: remoteUsername), + 'bob'); + }); }); // group Initialization }); // group SshnpCore } diff --git a/packages/noports_core/test/sshnp/sshnp_mocks.dart b/packages/noports_core/test/sshnp/sshnp_mocks.dart index 27667e4fc..fb54234dc 100644 --- a/packages/noports_core/test/sshnp/sshnp_mocks.dart +++ b/packages/noports_core/test/sshnp/sshnp_mocks.dart @@ -45,4 +45,3 @@ abstract class StartProcessCaller { } class StartProcessStub extends Mock implements StartProcessCaller {} - diff --git a/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart b/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart index e94eeb352..95df64a6a 100644 --- a/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart +++ b/packages/noports_core/test/sshnp/util/sshnp_ssh_key_handler/sshnp_local_ssh_key_handler_test.dart @@ -40,6 +40,9 @@ void main() { .thenAnswer((_) async {}); when(() => mockSshnpdChannel.resolveRemoteUsername()) .thenAnswer((_) async => 'myRemoteUsername'); + when(() => mockSshnpdChannel.resolveTunnelUsername( + remoteUsername: any(named: 'remoteUsername'))) + .thenAnswer((_) async => 'myTunnelUsername'); when(() => mockSshnpdChannel.sharePublicKeyIfRequired(identityKeyPair)) .thenAnswer((_) async {}); when(() => mockSshrvdChannel.callInitialization()) diff --git a/packages/sshnp_gui/lib/l10n/app_en.arb b/packages/sshnp_gui/lib/l10n/app_en.arb index 85999dda4..cb951e9c6 100644 --- a/packages/sshnp_gui/lib/l10n/app_en.arb +++ b/packages/sshnp_gui/lib/l10n/app_en.arb @@ -72,6 +72,7 @@ "success": "Success", "switchAtsign" : "Switch atSign", "to" : "To", + "tunnelUserName" : "Remote Username to use for initial ssh tunnel", "username" : "Username", "usernameHint": "The user name on this host", "verbose" : "Verbose Logging",