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

fix: terminal session and private key issues #688

Merged
merged 3 commits into from Jan 17, 2024
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -1,12 +1,16 @@
import 'dart:async';
import 'dart:developer';

import 'package:at_client_mobile/at_client_mobile.dart';
import 'package:biometric_storage/biometric_storage.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';
import 'package:noports_core/sshnp_params.dart';
import 'package:sshnp_flutter/src/controllers/navigation_controller.dart';
import 'package:sshnp_flutter/src/presentation/widgets/utility/custom_snack_bar.dart';
import 'package:sshnp_flutter/src/repository/private_key_manager_repository.dart';
import 'package:sshnp_flutter/src/repository/profile_private_key_manager_repository.dart';

import '../application/private_key_manager.dart';
import '../repository/navigation_repository.dart';
Expand Down Expand Up @@ -125,8 +129,23 @@ class AtSshKeyPairManagerFamilyController extends AutoDisposeFamilyAsyncNotifier

Future<void> deletePrivateKeyManager({required String identifier, BuildContext? context}) async {
try {
await PrivateKeyManagerRepository.deletePrivateKeyManager(arg);
// await PrivateKeyManagerRepository.deletePrivateKeyManager(arg);
ref.read(atPrivateKeyManagerListController.notifier).remove(arg);
// Read in profiles
AtClient atClient = AtClientManager.getInstance().atClient;
final profiles = await ConfigKeyRepository.listProfiles(atClient);

for (final profile in profiles) {
//delete profile private key manager that matches the deleted private key manager
final profilePrivateKeyManager = await ProfilePrivateKeyManagerRepository.readProfilePrivateKeyManager(profile);
log('arg: $arg, profile $profile, profile private manager key nickname: ${profilePrivateKeyManager.privateKeyNickname}, profilePrivateKeyManager profile name: ${profilePrivateKeyManager.profileNickname}');

if (profilePrivateKeyManager.privateKeyNickname == arg) {
log('profile from if clause: $profile');
await ProfilePrivateKeyManagerRepository.deleteProfilePrivateKeyManager(profile);
}
}

state = AsyncValue.error('SSHNPParams has been disposed', StackTrace.current);
} catch (e) {
if (context?.mounted ?? false) {
Expand Down
@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:developer';

import 'package:dartssh2/dartssh2.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
Expand Down Expand Up @@ -71,7 +72,8 @@ class TerminalSession {
String displayName;

bool isRunning = false;
bool isDisposed = true;
// TODO: delete this
// bool isDisposed = true;

SshnpRemoteProcess? shell;

Expand Down Expand Up @@ -104,7 +106,8 @@ class TerminalSessionFamilyController extends FamilyNotifier<TerminalSession, St

if (state.isRunning) return;
state.isRunning = true;
state.isDisposed = false;
//TODO: Delete this
// state.isDisposed = false;

if (terminalTitle != null) {
state.terminal.setTitle(terminalTitle);
Expand Down Expand Up @@ -160,8 +163,12 @@ class TerminalSessionFamilyController extends FamilyNotifier<TerminalSession, St
}

void dispose() {
log('dispose called');

/// If the session is already disposed, return null
if (state.isDisposed) return;
// TODO: delete this
// if (state.isDisposed) return;
log('dispose not disposed');

/// 1. Set the session to disposed
if (state.isRunning) _killProcess();
Expand Down
Expand Up @@ -45,6 +45,12 @@ class _ProfileTerminalActionState extends ConsumerState<ProfileTerminalAction> {
if (mounted) {
showProgress('Starting Shell Session...');
}

/// Issue a new session id
final sessionId = ref.watch(terminalSessionController.notifier).createSession();

/// Create the session controller for the new session id
final sessionController = ref.watch(terminalSessionFamilyController(sessionId).notifier);
// TODO: add try
try {
// TODO ensure that this keyPair gets uploaded to the app first
Expand Down Expand Up @@ -104,17 +110,11 @@ class _ProfileTerminalActionState extends ConsumerState<ProfileTerminalAction> {
throw result;
}

/// Issue a new session id
final sessionId = ref.watch(terminalSessionController.notifier).createSession();

/// Create the session controller for the new session id
final sessionController = ref.watch(terminalSessionFamilyController(sessionId).notifier);

if (result is SshnpCommand) {
if (sshnp.canRunShell) {
if (mounted) {
context.pop();
showProgress('running shell session');
showProgress('running shell session...');
}
log('running shell session...');

Expand All @@ -139,6 +139,7 @@ class _ProfileTerminalActionState extends ConsumerState<ProfileTerminalAction> {
}
//TODO: Add catch
} catch (e) {
sessionController.dispose();
if (mounted) {
log('error: ${e.toString()}');
context.pop();
Expand Down
Expand Up @@ -41,7 +41,9 @@ class _ProfileFormState extends ConsumerState<ProfileFormDesktopView> {
ref.read(formProfileNameController.notifier).state = currentProfile.profileName;
privateKeyNickname =
await ProfilePrivateKeyManagerRepository.readProfilePrivateKeyManager(currentProfile.profileName)
.then((value) => value?.privateKeyNickname);
.then((value) => value.privateKeyNickname);

if (privateKeyNickname == '') privateKeyNickname = null;
});

super.initState();
Expand Down Expand Up @@ -83,7 +85,6 @@ class _ProfileFormState extends ConsumerState<ProfileFormDesktopView> {
final strings = AppLocalizations.of(context)!;
currentProfile = ref.watch(currentConfigController);
final privateKeyManagerListController = ref.watch(atPrivateKeyManagerListController);
// final profilePrivateKeyListController = ref.watch(profilePrivateKeyManagerListController);

final asyncOldConfig = ref.watch(configFamilyController(currentProfile.profileName));

Expand Down Expand Up @@ -279,55 +280,10 @@ class _ProfileFormState extends ConsumerState<ProfileFormDesktopView> {
},
onValidator: FormValidator.validatePrivateKeyField,
);

// return CustomMultiSelectChipFormField<String>(
// width: kFieldDefaultWidth + Sizes.p5,
// selectedItems: selectedItems,
// label: strings.privateKey,
// hintText: strings.select,
// items: privateKeyList.toList(),
// onChanged: (value) {
// if (value == kPrivateKeyDropDownOption) {
// showDialog(
// context: context, builder: ((context) => const SSHKeyManagementFormDialog()));
// }
// },
// onSaved: (value) {
// final atSshKeyPair = ref.read(privateKeyManagerFamilyController(value!));
// // write to a new controller to map the profile name to the ssh key pair manager nickname. Store this information locally.

// // At the point of ssh connect the profile name and the key manager name so get the identity passphrase and the fiel content.
// atSshKeyPair.when(
// data: (data) => newConfig = SshnpPartialParams.merge(
// newConfig,
// SshnpPartialParams(
// identityFile: data.nickname, identityPassphrase: data.passPhrase)),
// error: ((error, stackTrace) => log(error.toString())),
// loading: () => const CircularProgressIndicator());
// },
// onValidator: FormValidator.validatePrivateKeyField,
// );
}),
gapH20,
// TODO: Add key management dropdown here
gapH20,

// CustomDropdownFormField<SupportedSshAlgorithm>(
// label: strings.sshAlgorithm,
// hintText: strings.select,
// initialValue: oldConfig.sshAlgorithm,
// items: SupportedSshAlgorithm.values
// .map((e) => DropdownMenuItem<SupportedSshAlgorithm>(
// value: e,
// child: Text(e.name),
// ))
// .toList(),
// onChanged: ((value) =>
// newConfig = SshnpPartialParams.merge(newConfig, SshnpPartialParams(sshAlgorithm: value))),
// ),
gapH10,
CustomSwitchWidget(
//TODO: change string to sendSshPublicKey not ssh public key
labelText: strings.sendSshPublicKey,
value: newConfig.sendSshPublicKey ?? oldConfig.sendSshPublicKey,
tooltip: strings.sendSshPublicKeyTooltip,
Expand Down
Expand Up @@ -7,26 +7,22 @@ import '../../../utility/constants.dart';
import 'ssh_key_management_form_dialog.dart';

class SshKeyPairBarActions extends ConsumerStatefulWidget {
const SshKeyPairBarActions({required this.identifier, Key? key})
: super(key: key);
const SshKeyPairBarActions({required this.identifier, Key? key}) : super(key: key);

final String identifier;

@override
ConsumerState<SshKeyPairBarActions> createState() =>
_SshKeyPairBarActionsState();
ConsumerState<SshKeyPairBarActions> createState() => _SshKeyPairBarActionsState();
}

class _SshKeyPairBarActionsState extends ConsumerState<SshKeyPairBarActions> {
@override
Widget build(BuildContext context) {
final controller = ref
.watch(privateKeyManagerFamilyController(widget.identifier).notifier);
final controller = ref.watch(privateKeyManagerFamilyController(widget.identifier).notifier);
return Row(
children: [
IconButton(
style:
FilledButton.styleFrom(backgroundColor: kIconColorBackgroundDark),
style: FilledButton.styleFrom(backgroundColor: kIconColorBackgroundDark),
onPressed: () async {
await showDialog(
context: context,
Expand All @@ -41,10 +37,9 @@ class _SshKeyPairBarActionsState extends ConsumerState<SshKeyPairBarActions> {
),
gapW8,
IconButton(
style:
FilledButton.styleFrom(backgroundColor: kIconColorBackgroundDark),
onPressed: () {
controller.deletePrivateKeyManager(identifier: widget.identifier);
style: FilledButton.styleFrom(backgroundColor: kIconColorBackgroundDark),
onPressed: () async {
await controller.deletePrivateKeyManager(identifier: widget.identifier);
},
icon: const Icon(
Icons.delete_outline,
Expand Down
Expand Up @@ -24,7 +24,7 @@ class _TerminalScreenDesktopViewState extends ConsumerState<TerminalScreenDeskto
super.dispose();
}

void closeSession(String sessionId, List<String> terminalList) {
void closeSession(String sessionId) {
// Remove the session from the list of sessions
final controller = ref.read(terminalSessionFamilyController(sessionId).notifier);
controller.dispose();
Expand Down Expand Up @@ -73,7 +73,7 @@ class _TerminalScreenDesktopViewState extends ConsumerState<TerminalScreenDeskto
Text(displayName),
IconButton(
icon: const Icon(Icons.close),
onPressed: () => closeSession(sessionId, terminalList),
onPressed: () => closeSession(sessionId),
)
],
),
Expand Down
4 changes: 2 additions & 2 deletions packages/dart/sshnp_flutter/lib/src/utility/constants.dart
Expand Up @@ -20,8 +20,8 @@ const kInputChipBackgroundColor = Color(0XFF515151);

const kEmptyFieldValidationError = 'Field cannot be left blank';
const kAtsignFieldValidationError = 'Field must start with @';
const kProfileNameFieldValidationError = 'Field must only use alphanumeric characters and spaces';
const kPrivateKeyFieldValidationError = 'Field must only use alphanumeric characters';
const kProfileNameFieldValidationError = 'Field must only use lower case alphanumeric characters spaces';
const kPrivateKeyFieldValidationError = 'Field must only use lower case alphanumeric characters';

const kPrivateKeyDropDownOption = 'Create a new private key';

Expand Down
Expand Up @@ -20,7 +20,7 @@ class FormValidator {
}

static String? validateProfileNameField(String? value) {
String invalid = '[^a-zA-Z0-9 ]';
String invalid = '[^a-z0-9 ]';
if (value?.isEmpty ?? true) {
return kEmptyFieldValidationError;
} else if (value!.contains(RegExp(invalid))) {
Expand All @@ -30,7 +30,7 @@ class FormValidator {
}

static String? validatePrivateKeyField(String? value) {
String invalid = '[^a-zA-Z0-9_]';
String invalid = '[^a-z0-9_]';
if (value?.isEmpty ?? true) {
return kEmptyFieldValidationError;
} else if (value! == kPrivateKeyDropDownOption) {
Expand Down