Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tunnelUsername parameter #586

Merged
merged 2 commits into from
Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions packages/noports_core/lib/src/sshnp/models/sshnp_arg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ class SshnpArg {
localSshOptionsArg,
verboseArg,
remoteUserNameArg,
tunnelUserNameArg,
rootDomainArg,
localSshdPortArg,
legacyDaemonArg,
Expand Down Expand Up @@ -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',
Expand Down
14 changes: 12 additions & 2 deletions packages/noports_core/lib/src/sshnp/models/sshnp_params.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class SshnpParams {
final bool sendSshPublicKey;
final List<String> localSshOptions;
final String? remoteUsername;
final String? tunnelUsername;
final bool verbose;
final String rootDomain;
final int localSshdPort;
Expand All @@ -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;
Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -229,6 +235,7 @@ class SshnpPartialParams {
final bool? sendSshPublicKey;
final List<String>? localSshOptions;
final String? remoteUsername;
final String? tunnelUsername;
final bool? verbose;
final String? rootDomain;
final bool? legacyDaemon;
Expand All @@ -255,6 +262,7 @@ class SshnpPartialParams {
this.sendSshPublicKey,
this.localSshOptions,
this.remoteUsername,
this.tunnelUsername,
this.verbose,
this.rootDomain,
this.localSshdPort,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -339,6 +348,7 @@ class SshnpPartialParams {
? null
: List<String>.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],
Expand Down
7 changes: 7 additions & 0 deletions packages/noports_core/lib/src/sshnp/sshnp_core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);
Expand All @@ -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,
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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}'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<String?> 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
Expand Down
2 changes: 1 addition & 1 deletion packages/noports_core/lib/sshrv.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
library noports_core_sshrv;

export 'src/sshrv/sshrv.dart';
export 'src/sshrv/sshrv.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,23 @@ void main() {
test('ConfigFileRepository.atKeyFromProfileName test', () async {
XavierChanth marked this conversation as resolved.
Show resolved Hide resolved
String profileName = 'myProfileName';

expect(ConfigFileRepository.getDefaultSshnpConfigDirectory(getHomeDirectory()!), isA<String>());
expect(ConfigFileRepository.fromProfileName(profileName), isA<Future<String>>());
expect(
ConfigFileRepository.getDefaultSshnpConfigDirectory(
getHomeDirectory()!),
isA<String>());
expect(ConfigFileRepository.fromProfileName(profileName),
isA<Future<String>>());
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]', () {
Expand Down
Loading
Loading