Adds Port parameter for SSH PSSessions (#3499)
* fixes summary block typo * Adds support for Port parameter for SSH PSSessions * Reverted back to master, modified based on PR feedback * Update exception message * remove unused line * Add existing constructor back in as to not break public contract * remove port check * pass nested inner exception straight to Should * dispose runspace after each test * Add SSHHostParameterSet attribute for Invoke-Command Port property * Update ParseSSHConnectionHashTable method to accept Port value as integer * Add helper method for validating port in range. Refactor port parameter constructor overload to use original constructor * rename method * Adds GetSSHConnectionStringParameter and GetSSHConnectionIntParameter methods for retrieving SSHConnection hashtable values * Adds method comments * Adds helper method comment * Change methods to add C# 7 patterns
This commit is contained in:
parent
ee9049b61f
commit
1d42862e21
|
@ -223,6 +223,7 @@ namespace Microsoft.PowerShell.Commands
|
|||
/// </remarks>
|
||||
[Parameter(ParameterSetName = InvokeCommandCommand.ComputerNameParameterSet)]
|
||||
[Parameter(ParameterSetName = InvokeCommandCommand.FilePathComputerNameParameterSet)]
|
||||
[Parameter(ParameterSetName = InvokeCommandCommand.SSHHostParameterSet)]
|
||||
[ValidateRange((Int32)1, (Int32)UInt16.MaxValue)]
|
||||
public override Int32 Port
|
||||
{
|
||||
|
|
|
@ -275,6 +275,7 @@ namespace Microsoft.PowerShell.Commands
|
|||
public string ComputerName;
|
||||
public string UserName;
|
||||
public string KeyFilePath;
|
||||
public int Port;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -561,6 +562,7 @@ namespace Microsoft.PowerShell.Commands
|
|||
/// to override the policy setting
|
||||
/// </remarks>
|
||||
[Parameter(ParameterSetName = PSRemotingBaseCmdlet.ComputerNameParameterSet)]
|
||||
[Parameter(ParameterSetName = PSRemotingBaseCmdlet.SSHHostParameterSet)]
|
||||
[ValidateRange((Int32)1, (Int32)UInt16.MaxValue)]
|
||||
public virtual Int32 Port { get; set; }
|
||||
|
||||
|
@ -820,6 +822,7 @@ namespace Microsoft.PowerShell.Commands
|
|||
private const string UserNameParameter = "UserName";
|
||||
private const string KeyFilePathParameter = "KeyFilePath";
|
||||
private const string IdentityFilePathAlias = "IdentityFilePath";
|
||||
private const string PortParameter = "Port";
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -851,25 +854,23 @@ namespace Microsoft.PowerShell.Commands
|
|||
throw new PSArgumentException(RemotingErrorIdStrings.InvalidSSHConnectionParameter);
|
||||
}
|
||||
|
||||
string paramValue = item[paramName] as string;
|
||||
if (string.IsNullOrEmpty(paramValue))
|
||||
{
|
||||
throw new PSArgumentException(RemotingErrorIdStrings.InvalidSSHConnectionParameter);
|
||||
}
|
||||
|
||||
if (paramName.Equals(ComputerNameParameter, StringComparison.OrdinalIgnoreCase) || paramName.Equals(HostNameAlias, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var resolvedComputerName = ResolveComputerName(paramValue);
|
||||
var resolvedComputerName = ResolveComputerName(GetSSHConnectionStringParameter(item[paramName]));
|
||||
ValidateComputerName(new string[] { resolvedComputerName });
|
||||
connectionInfo.ComputerName = resolvedComputerName;
|
||||
}
|
||||
else if (paramName.Equals(UserNameParameter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
connectionInfo.UserName = paramValue;
|
||||
connectionInfo.UserName = GetSSHConnectionStringParameter(item[paramName]);
|
||||
}
|
||||
else if (paramName.Equals(KeyFilePathParameter, StringComparison.OrdinalIgnoreCase) || paramName.Equals(IdentityFilePathAlias, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
connectionInfo.KeyFilePath = paramValue;
|
||||
connectionInfo.KeyFilePath = GetSSHConnectionStringParameter(item[paramName]);
|
||||
}
|
||||
else if (paramName.Equals(PortParameter, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
connectionInfo.Port = GetSSHConnectionIntParameter(item[paramName]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -981,6 +982,37 @@ namespace Microsoft.PowerShell.Commands
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates parameter value and returns as string
|
||||
/// </summary>
|
||||
/// <param name="param">Parameter value to be validated</param>
|
||||
/// <returns>Parameter value as string</returns>
|
||||
private static string GetSSHConnectionStringParameter(object param)
|
||||
{
|
||||
if (param is string paramValue && !string.IsNullOrEmpty(paramValue))
|
||||
{
|
||||
return paramValue;
|
||||
}
|
||||
|
||||
throw new PSArgumentException(RemotingErrorIdStrings.InvalidSSHConnectionParameter);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Validates parameter value and returns as integer
|
||||
/// </summary>
|
||||
/// <param name="param">Parameter value to be validated</param>
|
||||
/// <returns>Parameter value as integer</returns>
|
||||
private static int GetSSHConnectionIntParameter(object param)
|
||||
{
|
||||
if (param is int paramValue)
|
||||
{
|
||||
return paramValue;
|
||||
}
|
||||
|
||||
throw new PSArgumentException(RemotingErrorIdStrings.InvalidSSHConnectionParameter);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
|
||||
#region Overrides
|
||||
|
@ -1312,7 +1344,7 @@ namespace Microsoft.PowerShell.Commands
|
|||
|
||||
foreach (string computerName in ResolvedComputerNames)
|
||||
{
|
||||
var sshConnectionInfo = new SSHConnectionInfo(this.UserName, computerName, this.KeyFilePath);
|
||||
var sshConnectionInfo = new SSHConnectionInfo(this.UserName, computerName, this.KeyFilePath, this.Port);
|
||||
var typeTable = TypeTable.LoadDefaultTypeFiles();
|
||||
var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace;
|
||||
var pipeline = CreatePipeline(remoteRunspace);
|
||||
|
@ -1334,7 +1366,8 @@ namespace Microsoft.PowerShell.Commands
|
|||
var sshConnectionInfo = new SSHConnectionInfo(
|
||||
sshConnection.UserName,
|
||||
sshConnection.ComputerName,
|
||||
sshConnection.KeyFilePath);
|
||||
sshConnection.KeyFilePath,
|
||||
sshConnection.Port);
|
||||
var typeTable = TypeTable.LoadDefaultTypeFiles();
|
||||
var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace;
|
||||
var pipeline = CreatePipeline(remoteRunspace);
|
||||
|
|
|
@ -1270,7 +1270,7 @@ namespace Microsoft.PowerShell.Commands
|
|||
/// </summary>
|
||||
private RemoteRunspace GetRunspaceForSSHSession()
|
||||
{
|
||||
var sshConnectionInfo = new SSHConnectionInfo(this.UserName, ResolveComputerName(HostName), this.KeyFilePath);
|
||||
var sshConnectionInfo = new SSHConnectionInfo(this.UserName, ResolveComputerName(HostName), this.KeyFilePath, this.Port);
|
||||
var typeTable = TypeTable.LoadDefaultTypeFiles();
|
||||
var remoteRunspace = RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace;
|
||||
remoteRunspace.Open();
|
||||
|
|
|
@ -1078,7 +1078,8 @@ namespace Microsoft.PowerShell.Commands
|
|||
var sshConnectionInfo = new SSHConnectionInfo(
|
||||
this.UserName,
|
||||
computerName,
|
||||
this.KeyFilePath);
|
||||
this.KeyFilePath,
|
||||
this.Port);
|
||||
var typeTable = TypeTable.LoadDefaultTypeFiles();
|
||||
remoteRunspaces.Add(RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace);
|
||||
}
|
||||
|
@ -1095,7 +1096,8 @@ namespace Microsoft.PowerShell.Commands
|
|||
var sshConnectionInfo = new SSHConnectionInfo(
|
||||
sshConnection.UserName,
|
||||
sshConnection.ComputerName,
|
||||
sshConnection.KeyFilePath);
|
||||
sshConnection.KeyFilePath,
|
||||
sshConnection.Port);
|
||||
var typeTable = TypeTable.LoadDefaultTypeFiles();
|
||||
remoteRunspaces.Add(RunspaceFactory.CreateRunspace(sshConnectionInfo, this.Host, typeTable) as RemoteRunspace);
|
||||
}
|
||||
|
|
|
@ -355,6 +355,36 @@ namespace System.Management.Automation.Runspaces
|
|||
throw new PSNotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates port number is in range
|
||||
/// </summary>
|
||||
/// <param name="port">Port number to validate</param>
|
||||
internal virtual void ValidatePortInRange(int port)
|
||||
{
|
||||
if ((port < MinPort || port > MaxPort))
|
||||
{
|
||||
String message =
|
||||
PSRemotingErrorInvariants.FormatResourceString(
|
||||
RemotingErrorIdStrings.PortIsOutOfRange, port);
|
||||
ArgumentException e = new ArgumentException(message);
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Maximum value for port
|
||||
/// </summary>
|
||||
protected const int MaxPort = 0xFFFF;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum value for port
|
||||
/// </summary>
|
||||
protected const int MinPort = 0;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
@ -1172,6 +1202,7 @@ namespace System.Management.Automation.Runspaces
|
|||
|
||||
if (port.HasValue)
|
||||
{
|
||||
ValidatePortInRange(port.Value);
|
||||
// resolve to default ports if required
|
||||
if (port.Value == DefaultPort)
|
||||
{
|
||||
|
@ -1185,14 +1216,6 @@ namespace System.Management.Automation.Runspaces
|
|||
PortSetting = port.Value;
|
||||
UseDefaultWSManPort = false;
|
||||
}
|
||||
else if ((port.Value < MinPort || port.Value > MaxPort))
|
||||
{
|
||||
String message =
|
||||
PSRemotingErrorInvariants.FormatResourceString(
|
||||
RemotingErrorIdStrings.PortIsOutOfRange, port);
|
||||
ArgumentException e = new ArgumentException(message);
|
||||
throw e;
|
||||
}
|
||||
else
|
||||
{
|
||||
PortSetting = port.Value;
|
||||
|
@ -1391,16 +1414,6 @@ namespace System.Management.Automation.Runspaces
|
|||
/// </summary>
|
||||
private const string DefaultComputerName = "localhost";
|
||||
|
||||
/// <summary>
|
||||
/// Maximum value for port
|
||||
/// </summary>
|
||||
private const int MaxPort = 0xFFFF;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum value for port
|
||||
/// </summary>
|
||||
private const int MinPort = 0;
|
||||
|
||||
/// <summary>
|
||||
/// String that represents the local host Uri
|
||||
/// </summary>
|
||||
|
@ -1852,6 +1865,15 @@ namespace System.Management.Automation.Runspaces
|
|||
set;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Port for connection
|
||||
/// </summary>
|
||||
private int Port
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -1878,6 +1900,25 @@ namespace System.Management.Automation.Runspaces
|
|||
this.UserName = userName;
|
||||
this.ComputerName = computerName;
|
||||
this.KeyFilePath = keyFilePath;
|
||||
this.Port = DefaultPort;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="userName">User Name</param>
|
||||
/// <param name="computerName">Computer Name</param>
|
||||
/// <param name="keyFilePath">Key File Path</param>
|
||||
/// <param name="port">Port number for connection (default 22)</param>
|
||||
public SSHConnectionInfo(
|
||||
string userName,
|
||||
string computerName,
|
||||
string keyFilePath,
|
||||
int port) : this(userName, computerName, keyFilePath)
|
||||
{
|
||||
ValidatePortInRange(port);
|
||||
|
||||
this.Port = (port != 0) ? port : DefaultPort;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
@ -1930,6 +1971,7 @@ namespace System.Management.Automation.Runspaces
|
|||
newCopy.ComputerName = this.ComputerName;
|
||||
newCopy.UserName = this.UserName;
|
||||
newCopy.KeyFilePath = this.KeyFilePath;
|
||||
newCopy.Port = this.Port;
|
||||
|
||||
return newCopy;
|
||||
}
|
||||
|
@ -2004,14 +2046,14 @@ namespace System.Management.Automation.Runspaces
|
|||
}
|
||||
|
||||
arguments = (string.IsNullOrEmpty(domainName)) ?
|
||||
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" {1}@{2} -s powershell", this.KeyFilePath, userName, this.ComputerName) :
|
||||
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" -l {1}@{2} {3} -s powershell", this.KeyFilePath, userName, domainName, this.ComputerName);
|
||||
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" {1}@{2} -p {3} -s powershell", this.KeyFilePath, userName, this.ComputerName, this.Port) :
|
||||
string.Format(CultureInfo.InvariantCulture, @"-i ""{0}"" -l {1}@{2} {3} -p {4} -s powershell", this.KeyFilePath, userName, domainName, this.ComputerName, this.Port);
|
||||
}
|
||||
else
|
||||
{
|
||||
arguments = (string.IsNullOrEmpty(domainName)) ?
|
||||
string.Format(CultureInfo.InvariantCulture, @"{0}@{1} -s powershell", userName, this.ComputerName) :
|
||||
string.Format(CultureInfo.InvariantCulture, @"-l {0}@{1} {2} -s powershell", userName, domainName, this.ComputerName);
|
||||
string.Format(CultureInfo.InvariantCulture, @"{0}@{1} -p {2} -s powershell", userName, this.ComputerName, this.Port) :
|
||||
string.Format(CultureInfo.InvariantCulture, @"-l {0}@{1} {2} -p {3} -s powershell", userName, domainName, this.ComputerName, this.Port);
|
||||
}
|
||||
|
||||
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo(
|
||||
|
@ -2037,7 +2079,16 @@ namespace System.Management.Automation.Runspaces
|
|||
#endif
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Default value for port
|
||||
/// </summary>
|
||||
private const int DefaultPort = 22;
|
||||
|
||||
#endregion
|
||||
|
||||
#region SSH Process Creation
|
||||
|
||||
|
|
|
@ -1,22 +1,22 @@
|
|||
Import-Module $PSScriptRoot\..\..\Common\Test.Helpers.psm1
|
||||
|
||||
Describe "SSH Remoting API Tests" -Tags "Feature" {
|
||||
|
||||
Context "SSHConnectionInfo Class Tests" {
|
||||
|
||||
AfterEach {
|
||||
if ($rs -ne $null) {
|
||||
$rs.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
It "SSHConnectionInfo constructor should throw null argument exception for null HostName parameter" {
|
||||
|
||||
try
|
||||
{
|
||||
[System.Management.Automation.Runspaces.SSHConnectionInfo]::new(
|
||||
{ [System.Management.Automation.Runspaces.SSHConnectionInfo]::new(
|
||||
"UserName",
|
||||
[System.Management.Automation.Internal.AutomationNull]::Value,
|
||||
[System.Management.Automation.Internal.AutomationNull]::Value)
|
||||
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$_.FullyQualifiedErrorId | Should Be "PSArgumentNullException"
|
||||
}
|
||||
[System.Management.Automation.Internal.AutomationNull]::Value,
|
||||
0) } | ShouldBeErrorId "PSArgumentNullException"
|
||||
}
|
||||
|
||||
It "SSHConnectionInfo should throw file not found exception for invalid key file path" {
|
||||
|
@ -26,7 +26,8 @@ Describe "SSH Remoting API Tests" -Tags "Feature" {
|
|||
$sshConnectionInfo = [System.Management.Automation.Runspaces.SSHConnectionInfo]::new(
|
||||
"UserName",
|
||||
"localhost",
|
||||
"NoValidKeyFilePath")
|
||||
"NoValidKeyFilePath",
|
||||
22)
|
||||
|
||||
$rs = [runspacefactory]::CreateRunspace($sshConnectionInfo)
|
||||
$rs.Open()
|
||||
|
@ -35,7 +36,33 @@ Describe "SSH Remoting API Tests" -Tags "Feature" {
|
|||
}
|
||||
catch
|
||||
{
|
||||
$_.Exception.InnerException.InnerException | Should BeOfType System.IO.FileNotFoundException
|
||||
$_.Exception.InnerException.InnerException | Should BeOfType "System.IO.FileNotFoundException"
|
||||
}
|
||||
}
|
||||
|
||||
It "SSHConnectionInfo should throw argument exception for invalid port (non 16bit uint)" {
|
||||
try
|
||||
{
|
||||
$sshConnectionInfo = [System.Management.Automation.Runspaces.SSHConnectionInfo]::new(
|
||||
"UserName",
|
||||
"localhost",
|
||||
"ValidKeyFilePath",
|
||||
99999)
|
||||
|
||||
$rs = [runspacefactory]::CreateRunspace($sshConnectionInfo)
|
||||
$rs.Open()
|
||||
|
||||
throw "No Exception!"
|
||||
}
|
||||
catch
|
||||
{
|
||||
$expectedArgumentException = $_.Exception
|
||||
if ($_.Exception.InnerException -ne $null)
|
||||
{
|
||||
$expectedArgumentException = $_.Exception.InnerException
|
||||
}
|
||||
|
||||
$expectedArgumentException | Should BeOfType "System.ArgumentException"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue