sources/Google.Solutions.IapDesktop.Extensions.Session/ToolWindows/Session/SshKeyboardInteractiveHandler.cs (88 lines of code) (raw):

// // Copyright 2024 Google LLC // // Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. // using Google.Solutions.Apis.Locator; using Google.Solutions.IapDesktop.Application.Windows.Dialog; using Google.Solutions.Ssh; using System; using System.ComponentModel; using System.Diagnostics; using System.Windows.Forms; namespace Google.Solutions.IapDesktop.Extensions.Session.ToolWindows.Session { internal class SshKeyboardInteractiveHandler : IKeyboardInteractiveHandler { private readonly IWin32Window owner; private readonly IInputDialog inputDialog; private readonly InstanceLocator instance; /// <summary> /// Check if the caller thread is different from the UI thread. /// </summary> private bool InvokeRequired { get => (this.owner as ISynchronizeInvoke)?.InvokeRequired == true; } private void ValidateInputIsNotEmpty( string input, out bool valid, out string? warning) { valid = !string.IsNullOrEmpty(input); warning = null; } public SshKeyboardInteractiveHandler( IWin32Window owner, IInputDialog inputDialog, InstanceLocator instance) { this.owner = owner; this.inputDialog = inputDialog; this.instance = instance; } //---------------------------------------------------------------------- // IKeyboardInteractiveHandler. //---------------------------------------------------------------------- public string? Prompt(string caption, string instruction, string prompt, bool echo) { Debug.Assert(!this.InvokeRequired, "On UI thread"); if (this.inputDialog.Prompt( this.owner, new InputDialogParameters() { Title = this.instance.Name, Caption = caption, IsPassword = !echo, Message = prompt, Validate = ValidateInputIsNotEmpty }, out var userInput) == DialogResult.OK && userInput != null) { // // NB. The input dialog won't give us a null or empty result // because of the validation callback we use. // Debug.Assert(!string.IsNullOrEmpty(userInput)); userInput = userInput.Trim(); // // Strip: // - spaces between group of digits (g.co/sc) // - "G-" prefix (text messages) // if (userInput.StartsWith("g-", StringComparison.OrdinalIgnoreCase)) { userInput = userInput.Substring(2); } return userInput.Replace(" ", string.Empty); } else { throw new OperationCanceledException(); } } public IPasswordCredential PromptForCredentials(string username) { Debug.Assert(!this.InvokeRequired, "On UI thread"); if (this.inputDialog.Prompt( this.owner, new InputDialogParameters() { Title = this.instance.Name, Caption = "Enter password for " + username, IsPassword = true, Message = "These credentials will be used to connect to " + this.instance.Name, Validate = ValidateInputIsNotEmpty }, out var userInput) == DialogResult.OK && userInput != null) { return new StaticPasswordCredential(username, userInput.Trim()); } else { throw new OperationCanceledException(); } } } }