Skip to content

Commit ca12001

Browse files
PaulHigindaxian-dbw
authored andcommitted
Implement RoleDefinitions RoleCapabilityFiles keyword (PowerShell#3067)
* Changes to implement remote endpoint RoleDefinition RoleCapabilityFiles keyword * Fixed spelling error. Added back missing resource string * Updated tests from CR comments * Simplified error tests per Code Review * Test change from Code Review
1 parent a16fead commit ca12001

4 files changed

Lines changed: 162 additions & 6 deletions

File tree

src/System.Management.Automation/engine/remoting/commands/NewPSSessionConfigurationFile.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,7 @@ protected override void ProcessRecord()
735735
if (_roleDefinitions == null)
736736
{
737737
result.Append(SessionConfigurationUtils.ConfigFragment(ConfigFileConstants.RoleDefinitions, RemotingErrorIdStrings.DISCRoleDefinitionsComment,
738-
"@{ 'CONTOSO\\SqlAdmins' = @{ RoleCapabilities = 'SqlAdministration' }; 'CONTOSO\\ServerMonitors' = @{ VisibleCmdlets = 'Get-Process' } } ", streamWriter, true));
738+
"@{ 'CONTOSO\\SqlAdmins' = @{ RoleCapabilities = 'SqlAdministration' }; 'CONTOSO\\SqlManaged' = @{ RoleCapabilityFiles = 'C:\\RoleCapability\\SqlManaged.psrc' }; 'CONTOSO\\ServerMonitors' = @{ VisibleCmdlets = 'Get-Process' } } ", streamWriter, true));
739739
}
740740
else
741741
{

src/System.Management.Automation/engine/remoting/fanin/InitialSessionStateProvider.cs

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,7 @@ internal static class ConfigFileConstants
947947
internal static readonly string ScriptsToProcess = "ScriptsToProcess";
948948
internal static readonly string SessionType = "SessionType";
949949
internal static readonly string RoleCapabilities = "RoleCapabilities";
950+
internal static readonly string RoleCapabilityFiles = "RoleCapabilityFiles";
950951
internal static readonly string RunAsVirtualAccount = "RunAsVirtualAccount";
951952
internal static readonly string RunAsVirtualAccountGroups = "RunAsVirtualAccountGroups";
952953
internal static readonly string TranscriptDirectory = "TranscriptDirectory";
@@ -981,6 +982,7 @@ internal static class ConfigFileConstants
981982
new ConfigTypeEntry(PowerShellVersion, new ConfigTypeEntry.TypeValidationCallback(StringTypeValidationCallback)),
982983
new ConfigTypeEntry(RequiredGroups, new ConfigTypeEntry.TypeValidationCallback(HashtableTypeValidationCallback)),
983984
new ConfigTypeEntry(RoleCapabilities, new ConfigTypeEntry.TypeValidationCallback(StringArrayTypeValidationCallback)),
985+
new ConfigTypeEntry(RoleCapabilityFiles, new ConfigTypeEntry.TypeValidationCallback(StringArrayTypeValidationCallback)),
984986
new ConfigTypeEntry(RoleDefinitions, new ConfigTypeEntry.TypeValidationCallback(HashtableTypeValidationCallback)),
985987
new ConfigTypeEntry(RunAsVirtualAccount, new ConfigTypeEntry.TypeValidationCallback(BooleanTypeValidationCallback)),
986988
new ConfigTypeEntry(RunAsVirtualAccountGroups, new ConfigTypeEntry.TypeValidationCallback(StringArrayTypeValidationCallback)),
@@ -1391,6 +1393,7 @@ internal static class DISCUtils
13911393
private static readonly HashSet<string> s_allowedRoleCapabilityKeys = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
13921394
{
13931395
"RoleCapabilities",
1396+
"RoleCapabilityFiles",
13941397
"ModulesToImport",
13951398
"VisibleAliases",
13961399
"VisibleCmdlets",
@@ -1808,8 +1811,11 @@ private void MergeRoleRulesIntoConfigHash(Func<string, bool> roleVerifier)
18081811
}
18091812

18101813
// Takes the "RoleCapabilities" node in the config hash, and merges its values into the base configuration.
1814+
private const string PSRCExtension = ".psrc";
18111815
private void MergeRoleCapabilitiesIntoConfigHash()
18121816
{
1817+
List<string> psrcFiles = new List<string>();
1818+
18131819
if (_configHash.ContainsKey(ConfigFileConstants.RoleCapabilities))
18141820
{
18151821
string[] roleCapabilities = TryGetStringArray(_configHash[ConfigFileConstants.RoleCapabilities]);
@@ -1821,18 +1827,51 @@ private void MergeRoleCapabilitiesIntoConfigHash()
18211827
string roleCapabilityPath = GetRoleCapabilityPath(roleCapability);
18221828
if (String.IsNullOrEmpty(roleCapabilityPath))
18231829
{
1824-
string message = StringUtil.Format(RemotingErrorIdStrings.CouldNotFindRoleCapability, roleCapability, roleCapability + ".psrc");
1830+
string message = StringUtil.Format(RemotingErrorIdStrings.CouldNotFindRoleCapability, roleCapability, roleCapability + PSRCExtension);
18251831
PSInvalidOperationException ioe = new PSInvalidOperationException(message);
18261832
ioe.SetErrorId("CouldNotFindRoleCapability");
18271833
throw ioe;
18281834
}
18291835

1830-
DISCPowerShellConfiguration roleCapabilityConfiguration = new DISCPowerShellConfiguration(roleCapabilityPath, null);
1831-
IDictionary roleCapabilityConfigurationItems = roleCapabilityConfiguration.ConfigHash;
1832-
MergeConfigHashIntoConfigHash(roleCapabilityConfigurationItems);
1836+
psrcFiles.Add(roleCapabilityPath);
18331837
}
18341838
}
18351839
}
1840+
1841+
if (ConfigHash.ContainsKey(ConfigFileConstants.RoleCapabilityFiles))
1842+
{
1843+
string[] roleCapabilityFiles = TryGetStringArray(ConfigHash[ConfigFileConstants.RoleCapabilityFiles]);
1844+
if (roleCapabilityFiles != null)
1845+
{
1846+
foreach (var roleCapabilityFilePath in roleCapabilityFiles)
1847+
{
1848+
if (!Path.GetExtension(roleCapabilityFilePath).Equals(PSRCExtension, StringComparison.OrdinalIgnoreCase))
1849+
{
1850+
string message = StringUtil.Format(RemotingErrorIdStrings.InvalidRoleCapabilityFileExtension, roleCapabilityFilePath);
1851+
PSInvalidOperationException ioe = new PSInvalidOperationException(message);
1852+
ioe.SetErrorId("InvalidRoleCapabilityFileExtension");
1853+
throw ioe;
1854+
}
1855+
1856+
if (!File.Exists(roleCapabilityFilePath))
1857+
{
1858+
string message = StringUtil.Format(RemotingErrorIdStrings.CouldNotFindRoleCapabilityFile, roleCapabilityFilePath);
1859+
PSInvalidOperationException ioe = new PSInvalidOperationException(message);
1860+
ioe.SetErrorId("CouldNotFindRoleCapabilityFile");
1861+
throw ioe;
1862+
}
1863+
1864+
psrcFiles.Add(roleCapabilityFilePath);
1865+
}
1866+
}
1867+
}
1868+
1869+
foreach (var roleCapabilityFile in psrcFiles)
1870+
{
1871+
DISCPowerShellConfiguration roleCapabilityConfiguration = new DISCPowerShellConfiguration(roleCapabilityFile, null);
1872+
IDictionary roleCapabilityConfigurationItems = roleCapabilityConfiguration.ConfigHash;
1873+
MergeConfigHashIntoConfigHash(roleCapabilityConfigurationItems);
1874+
}
18361875
}
18371876

18381877
// Merge a role / role capability hashtable into the master configuration hashtable

src/System.Management.Automation/resources/RemotingErrorIdStrings.resx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -936,7 +936,7 @@ Do you want to continue?</value>
936936
<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.
937937
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>
938938
</data>
939-
<data name="WinRMForceRestartWarning" xml:space="preserve">
939+
<data name="WinRMForceRestartWarning" xml:space="preserve">
940940
<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>
941941
</data>
942942
<data name="JobSourceAdapterCannotSaveNullJob" xml:space="preserve">
@@ -1636,4 +1636,10 @@ All WinRM sessions connected to Windows PowerShell session configurations, such
16361636
<data name="SSHConnectionDuplicateKeyPath" xml:space="preserve">
16371637
<value>The provided SSHConnection hashtable contains both a KeyFilePath and IdentityFilePath parameter. Only one can be specified.</value>
16381638
</data>
1639+
<data name="CouldNotFindRoleCapabilityFile" xml:space="preserve">
1640+
<value>Could not find the provided role capability file {0}.</value>
1641+
</data>
1642+
<data name="InvalidRoleCapabilityFileExtension" xml:space="preserve">
1643+
<value>The provided role capability file {0} does not have the required .psrc extension.</value>
1644+
</data>
16391645
</root>
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
##
2+
## PowerShell Remoting Endpoint Role Capability Files Tests
3+
##
4+
5+
Describe "Remote session configuration RoleDefintion RoleCapabilityFiles key tests" -Tags "Feature" {
6+
7+
BeforeAll {
8+
9+
if (!$IsWindows)
10+
{
11+
$originalDefaultParameterValues = $PSDefaultParameterValues.Clone()
12+
$PSDefaultParameterValues["it:skip"] = $true
13+
}
14+
else
15+
{
16+
[string] $RoleCapDirectory = (New-Item -Path "$TestDrive\RoleCapability" -ItemType Directory -Force).FullName
17+
18+
[string] $GoodRoleCapFile = "$RoleCapDirectory\TestGoodRoleCap.psrc"
19+
New-PSRoleCapabilityFile -Path $GoodRoleCapFile -VisibleCmdlets 'Get-Command','Get-Process','Clear-Host','Out-Default','Select-Object','Get-FormatData','Get-Help'
20+
21+
[string] $BadRoleCapFile = "$RoleCapDirectory\TestBadRoleCap.psrc"
22+
New-PSRoleCapabilityFile -Path $BadRoleCapFile -VisibleCmdlets *
23+
[string] $BadRoleCapFile = $BadRoleCapFile.Replace('.psrc', 'psbad')
24+
25+
[string] $PSSessionConfigFile = "$RoleCapDirectory\TestConfig.pssc"
26+
}
27+
}
28+
29+
AfterAll {
30+
31+
if (!$IsWindows)
32+
{
33+
$global:PSDefaultParameterValues = $originalDefaultParameterValues
34+
}
35+
}
36+
37+
It "Verifies missing role capability file error" {
38+
39+
New-PSSessionConfigurationFile -Path $PSSessionConfigFile -RoleDefinitions @{
40+
Administrators = @{ RoleCapabilityFiles = "$RoleCapDirectory\NoFile.psrc" }
41+
}
42+
43+
$fullyQualifiedErrorId = ""
44+
try
45+
{
46+
$iss = [initialsessionstate]::CreateFromSessionConfigurationFile($PSSessionConfigFile, { $true })
47+
throw 'No Exception!'
48+
}
49+
catch
50+
{
51+
$psioe = [System.Management.Automation.PSInvalidOperationException] ($_.Exception).InnerException
52+
if ($psioe -ne $null)
53+
{
54+
$fullyQualifiedErrorId = $psioe.ErrorRecord.FullyQualifiedErrorId
55+
}
56+
$fullyQualifiedErrorId | Should Be 'CouldNotFindRoleCapabilityFile'
57+
}
58+
}
59+
60+
It "Verifies incorrect role capability file extenstion error" {
61+
62+
New-PSSessionConfigurationFile -Path $PSSessionConfigFile -RoleDefinitions @{
63+
Administrators = @{ RoleCapabilityFiles = "$BadRoleCapFile" }
64+
}
65+
66+
$fullyQualifiedErrorId = ""
67+
try
68+
{
69+
$iss = [initialsessionstate]::CreateFromSessionConfigurationFile($PSSessionConfigFile, { $true })
70+
throw 'No Exception!'
71+
}
72+
catch
73+
{
74+
$psioe = [System.Management.Automation.PSInvalidOperationException] ($_.Exception).InnerException
75+
if ($psioe -ne $null)
76+
{
77+
$fullyQualifiedErrorId = $psioe.ErrorRecord.FullyQualifiedErrorId
78+
}
79+
$fullyQualifiedErrorId | Should Be 'InvalidRoleCapabilityFileExtension'
80+
}
81+
}
82+
83+
It "Verifies restriction on good role capability file" {
84+
85+
New-PSSessionConfigurationFile -Path $PSSessionConfigFile -RoleDefinitions @{
86+
Administrators = @{ RoleCapabilityFiles = "$GoodRoleCapFile" }
87+
}
88+
89+
# 'Get-Service' is not included in the session.
90+
$iss = [initialsessionstate]::CreateFromSessionConfigurationFile($PSSessionConfigFile, { $true })
91+
[powershell] $ps = [powershell]::Create($iss)
92+
$null = $ps.AddCommand('Get-Service')
93+
94+
$exceptionTypeName = ""
95+
try
96+
{
97+
$ps.Invoke()
98+
throw 'No Exception!'
99+
}
100+
catch
101+
{
102+
if ($_.Exception.InnerException -ne $null)
103+
{
104+
$exceptionTypeName = $_.Exception.InnerException.GetType().FullName
105+
}
106+
$exceptionTypeName | Should Be 'System.Management.Automation.CommandNotFoundException'
107+
}
108+
109+
$ps.Dispose()
110+
}
111+
}

0 commit comments

Comments
 (0)