Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ protected override void ProcessRecord()
if (_roleDefinitions == null)
{
result.Append(SessionConfigurationUtils.ConfigFragment(ConfigFileConstants.RoleDefinitions, RemotingErrorIdStrings.DISCRoleDefinitionsComment,
"@{ 'CONTOSO\\SqlAdmins' = @{ RoleCapabilities = 'SqlAdministration' }; 'CONTOSO\\ServerMonitors' = @{ VisibleCmdlets = 'Get-Process' } } ", streamWriter, true));
"@{ 'CONTOSO\\SqlAdmins' = @{ RoleCapabilities = 'SqlAdministration' }; 'CONTOSO\\SqlManaged' = @{ RoleCapabilityFiles = 'C:\\RoleCapability\\SqlManaged.psrc' }; 'CONTOSO\\ServerMonitors' = @{ VisibleCmdlets = 'Get-Process' } } ", streamWriter, true));
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice windows specific paths (C:\\) are used in the code, and I assume the related functionalities are not supported on unix plats, but what would be the user experience? New-PSSessionConfigurationFile is exposed on unix plats, so will it throw a friendly error when running the following?

New-PSSessionConfigurationFile -Path $PSSessionConfigFile -RoleDefinitions @{
           Administrators = @{ RoleCapabilityFiles = "$RoleCapDirectory\NoFile.psrc" }
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remoting endpoint configuration is supported on Windows only, at least for now, hence the Windows only test restriction. This change follows the existing code as originally ported.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean this cmdlet shouldn't be exposed on Linux/OSX at all? I opened #3147 to track this usability issue.
The code change LGTM.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, none of our remoting endpoint configuration works on Linux since it is specific to Windows/WinRM, and is not functional with OMI/WinRM AFAIK. I hope to create an RFC in the not too distant future that proposes how this might work on Linux, including JEA and hosting model.

Can you please make this issue more general since it involves more than just one cmdlet and is really about endpoint configuration on Linux.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the clarification. I updated the issue to make it more general. Feel free to update further to make it more clear.

}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,7 @@ internal static class ConfigFileConstants
internal static readonly string ScriptsToProcess = "ScriptsToProcess";
internal static readonly string SessionType = "SessionType";
internal static readonly string RoleCapabilities = "RoleCapabilities";
internal static readonly string RoleCapabilityFiles = "RoleCapabilityFiles";
internal static readonly string RunAsVirtualAccount = "RunAsVirtualAccount";
internal static readonly string RunAsVirtualAccountGroups = "RunAsVirtualAccountGroups";
internal static readonly string TranscriptDirectory = "TranscriptDirectory";
Expand Down Expand Up @@ -981,6 +982,7 @@ internal static class ConfigFileConstants
new ConfigTypeEntry(PowerShellVersion, new ConfigTypeEntry.TypeValidationCallback(StringTypeValidationCallback)),
new ConfigTypeEntry(RequiredGroups, new ConfigTypeEntry.TypeValidationCallback(HashtableTypeValidationCallback)),
new ConfigTypeEntry(RoleCapabilities, new ConfigTypeEntry.TypeValidationCallback(StringArrayTypeValidationCallback)),
new ConfigTypeEntry(RoleCapabilityFiles, new ConfigTypeEntry.TypeValidationCallback(StringArrayTypeValidationCallback)),
new ConfigTypeEntry(RoleDefinitions, new ConfigTypeEntry.TypeValidationCallback(HashtableTypeValidationCallback)),
new ConfigTypeEntry(RunAsVirtualAccount, new ConfigTypeEntry.TypeValidationCallback(BooleanTypeValidationCallback)),
new ConfigTypeEntry(RunAsVirtualAccountGroups, new ConfigTypeEntry.TypeValidationCallback(StringArrayTypeValidationCallback)),
Expand Down Expand Up @@ -1391,6 +1393,7 @@ internal static class DISCUtils
private static readonly HashSet<string> s_allowedRoleCapabilityKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
"RoleCapabilities",
"RoleCapabilityFiles",
"ModulesToImport",
"VisibleAliases",
"VisibleCmdlets",
Expand Down Expand Up @@ -1808,8 +1811,11 @@ private void MergeRoleRulesIntoConfigHash(Func<string, bool> roleVerifier)
}

// Takes the "RoleCapabilities" node in the config hash, and merges its values into the base configuration.
private const string PSRCExtension = ".psrc";
private void MergeRoleCapabilitiesIntoConfigHash()
{
List<string> psrcFiles = new List<string>();

if (_configHash.ContainsKey(ConfigFileConstants.RoleCapabilities))
{
string[] roleCapabilities = TryGetStringArray(_configHash[ConfigFileConstants.RoleCapabilities]);
Expand All @@ -1821,18 +1827,51 @@ private void MergeRoleCapabilitiesIntoConfigHash()
string roleCapabilityPath = GetRoleCapabilityPath(roleCapability);
if (String.IsNullOrEmpty(roleCapabilityPath))
{
string message = StringUtil.Format(RemotingErrorIdStrings.CouldNotFindRoleCapability, roleCapability, roleCapability + ".psrc");
string message = StringUtil.Format(RemotingErrorIdStrings.CouldNotFindRoleCapability, roleCapability, roleCapability + PSRCExtension);
PSInvalidOperationException ioe = new PSInvalidOperationException(message);
ioe.SetErrorId("CouldNotFindRoleCapability");
throw ioe;
}

DISCPowerShellConfiguration roleCapabilityConfiguration = new DISCPowerShellConfiguration(roleCapabilityPath, null);
IDictionary roleCapabilityConfigurationItems = roleCapabilityConfiguration.ConfigHash;
MergeConfigHashIntoConfigHash(roleCapabilityConfigurationItems);
psrcFiles.Add(roleCapabilityPath);
}
}
}

if (ConfigHash.ContainsKey(ConfigFileConstants.RoleCapabilityFiles))
{
string[] roleCapabilityFiles = TryGetStringArray(ConfigHash[ConfigFileConstants.RoleCapabilityFiles]);
if (roleCapabilityFiles != null)
{
foreach (var roleCapabilityFilePath in roleCapabilityFiles)
{
if (!Path.GetExtension(roleCapabilityFilePath).Equals(PSRCExtension, StringComparison.OrdinalIgnoreCase))
{
string message = StringUtil.Format(RemotingErrorIdStrings.InvalidRoleCapabilityFileExtension, roleCapabilityFilePath);
PSInvalidOperationException ioe = new PSInvalidOperationException(message);
ioe.SetErrorId("InvalidRoleCapabilityFileExtension");
throw ioe;
}

if (!File.Exists(roleCapabilityFilePath))
{
string message = StringUtil.Format(RemotingErrorIdStrings.CouldNotFindRoleCapabilityFile, roleCapabilityFilePath);
PSInvalidOperationException ioe = new PSInvalidOperationException(message);
ioe.SetErrorId("CouldNotFindRoleCapabilityFile");
throw ioe;
}

psrcFiles.Add(roleCapabilityFilePath);
}
}
}

foreach (var roleCapabilityFile in psrcFiles)
{
DISCPowerShellConfiguration roleCapabilityConfiguration = new DISCPowerShellConfiguration(roleCapabilityFile, null);
IDictionary roleCapabilityConfigurationItems = roleCapabilityConfiguration.ConfigHash;
MergeConfigHashIntoConfigHash(roleCapabilityConfigurationItems);
}
}

// Merge a role / role capability hashtable into the master configuration hashtable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -936,7 +936,7 @@ Do you want to continue?</value>
<value>{0} may need to restart the WinRM service if a configuration using this name has recently been unregistered, certain system data structures may still be cached. In that case, a restart of WinRM may be required.
All WinRM sessions connected to Windows PowerShell session configurations, such as Microsoft.PowerShell and session configurations that are created with the Register-PSSessionConfiguration cmdlet, are disconnected.</value>
</data>
<data name="WinRMForceRestartWarning" xml:space="preserve">
<data name="WinRMForceRestartWarning" xml:space="preserve">
<value>You are running in a remote session and have selected the Force option which means the WinRM service may restart.If the WinRM service restarts then this remote session will be terminated and you will need to create a new session to continue</value>
</data>
<data name="JobSourceAdapterCannotSaveNullJob" xml:space="preserve">
Expand Down Expand Up @@ -1636,4 +1636,10 @@ All WinRM sessions connected to Windows PowerShell session configurations, such
<data name="SSHConnectionDuplicateKeyPath" xml:space="preserve">
<value>The provided SSHConnection hashtable contains both a KeyFilePath and IdentityFilePath parameter. Only one can be specified.</value>
</data>
<data name="CouldNotFindRoleCapabilityFile" xml:space="preserve">
<value>Could not find the provided role capability file {0}.</value>
</data>
<data name="InvalidRoleCapabilityFileExtension" xml:space="preserve">
<value>The provided role capability file {0} does not have the required .psrc extension.</value>
</data>
</root>
111 changes: 111 additions & 0 deletions test/powershell/engine/Remoting/RoleCapabilityFiles.Tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
##
## PowerShell Remoting Endpoint Role Capability Files Tests
##

Describe "Remote session configuration RoleDefintion RoleCapabilityFiles key tests" -Tags "Feature" {

BeforeAll {

if (!$IsWindows)
{
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
$PSDefaultParameterValues["it:skip"] = $true
}
else
{
[string] $RoleCapDirectory = (New-Item -Path "$TestDrive\RoleCapability" -ItemType Directory -Force).FullName

[string] $GoodRoleCapFile = "$RoleCapDirectory\TestGoodRoleCap.psrc"
New-PSRoleCapabilityFile -Path $GoodRoleCapFile -VisibleCmdlets 'Get-Command','Get-Process','Clear-Host','Out-Default','Select-Object','Get-FormatData','Get-Help'

[string] $BadRoleCapFile = "$RoleCapDirectory\TestBadRoleCap.psrc"
New-PSRoleCapabilityFile -Path $BadRoleCapFile -VisibleCmdlets *
[string] $BadRoleCapFile = $BadRoleCapFile.Replace('.psrc', 'psbad')

[string] $PSSessionConfigFile = "$RoleCapDirectory\TestConfig.pssc"
}
}

AfterAll {

if (!$IsWindows)
{
$global:PSDefaultParameterValues = $originalDefaultParameterValues
}
}

It "Verifies missing role capability file error" {

New-PSSessionConfigurationFile -Path $PSSessionConfigFile -RoleDefinitions @{
Administrators = @{ RoleCapabilityFiles = "$RoleCapDirectory\NoFile.psrc" }
}

$fullyQualifiedErrorId = ""
try
{
$iss = [initialsessionstate]::CreateFromSessionConfigurationFile($PSSessionConfigFile, { $true })
throw 'No Exception!'
}
catch
{
$psioe = [System.Management.Automation.PSInvalidOperationException] ($_.Exception).InnerException
if ($psioe -ne $null)
{
$fullyQualifiedErrorId = $psioe.ErrorRecord.FullyQualifiedErrorId
}
$fullyQualifiedErrorId | Should Be 'CouldNotFindRoleCapabilityFile'
}
}

It "Verifies incorrect role capability file extenstion error" {

New-PSSessionConfigurationFile -Path $PSSessionConfigFile -RoleDefinitions @{
Administrators = @{ RoleCapabilityFiles = "$BadRoleCapFile" }
}

$fullyQualifiedErrorId = ""
try
{
$iss = [initialsessionstate]::CreateFromSessionConfigurationFile($PSSessionConfigFile, { $true })
throw 'No Exception!'
}
catch
{
$psioe = [System.Management.Automation.PSInvalidOperationException] ($_.Exception).InnerException
if ($psioe -ne $null)
{
$fullyQualifiedErrorId = $psioe.ErrorRecord.FullyQualifiedErrorId
}
$fullyQualifiedErrorId | Should Be 'InvalidRoleCapabilityFileExtension'
}
}

It "Verifies restriction on good role capability file" {

New-PSSessionConfigurationFile -Path $PSSessionConfigFile -RoleDefinitions @{
Administrators = @{ RoleCapabilityFiles = "$GoodRoleCapFile" }
}

# 'Get-Service' is not included in the session.
$iss = [initialsessionstate]::CreateFromSessionConfigurationFile($PSSessionConfigFile, { $true })
[powershell] $ps = [powershell]::Create($iss)
$null = $ps.AddCommand('Get-Service')

$exceptionTypeName = ""
try
{
$ps.Invoke()
throw 'No Exception!'
}
catch
{
if ($_.Exception.InnerException -ne $null)
{
$exceptionTypeName = $_.Exception.InnerException.GetType().FullName
}
$exceptionTypeName | Should Be 'System.Management.Automation.CommandNotFoundException'
}

$ps.Dispose()
}
}