diff --git a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs index 74c394ddb64..503a17e5b3d 100644 --- a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs +++ b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs @@ -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)); } else { diff --git a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs index b2e20c02073..c11c23ffebc 100644 --- a/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs +++ b/src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs @@ -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"; @@ -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)), @@ -1391,6 +1393,7 @@ internal static class DISCUtils private static readonly HashSet s_allowedRoleCapabilityKeys = new HashSet(StringComparer.OrdinalIgnoreCase) { "RoleCapabilities", + "RoleCapabilityFiles", "ModulesToImport", "VisibleAliases", "VisibleCmdlets", @@ -1808,8 +1811,11 @@ private void MergeRoleRulesIntoConfigHash(Func 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 psrcFiles = new List(); + if (_configHash.ContainsKey(ConfigFileConstants.RoleCapabilities)) { string[] roleCapabilities = TryGetStringArray(_configHash[ConfigFileConstants.RoleCapabilities]); @@ -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 diff --git a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx index 69c3a356605..306e9dcadef 100644 --- a/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx +++ b/src/System.Management.Automation/resources/RemotingErrorIdStrings.resx @@ -936,7 +936,7 @@ Do you want to continue? {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. - + 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 @@ -1636,4 +1636,10 @@ All WinRM sessions connected to Windows PowerShell session configurations, such The provided SSHConnection hashtable contains both a KeyFilePath and IdentityFilePath parameter. Only one can be specified. + + Could not find the provided role capability file {0}. + + + The provided role capability file {0} does not have the required .psrc extension. + \ No newline at end of file diff --git a/test/powershell/engine/Remoting/RoleCapabilityFiles.Tests.ps1 b/test/powershell/engine/Remoting/RoleCapabilityFiles.Tests.ps1 new file mode 100644 index 00000000000..8ced5d266c4 --- /dev/null +++ b/test/powershell/engine/Remoting/RoleCapabilityFiles.Tests.ps1 @@ -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() + } +}