diff --git a/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs b/src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs index 74c394ddb6..503a17e5b3 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 @@ namespace Microsoft.PowerShell.Commands 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 b2e20c0207..c11c23ffeb 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 @@ namespace System.Management.Automation.Remoting 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 @@ namespace System.Management.Automation.Remoting 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 @@ namespace System.Management.Automation.Remoting private static readonly HashSet s_allowedRoleCapabilityKeys = new HashSet(StringComparer.OrdinalIgnoreCase) { "RoleCapabilities", + "RoleCapabilityFiles", "ModulesToImport", "VisibleAliases", "VisibleCmdlets", @@ -1808,8 +1811,11 @@ namespace System.Management.Automation.Remoting } // 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 @@ namespace System.Management.Automation.Remoting 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 69c3a35660..306e9dcade 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 0000000000..8ced5d266c --- /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() + } +}