如何确定字符串是 NT 帐户还是安全标识符?
How to determine if a string is an NT Account or a Security Identifier?
问题
我如何可靠地判断给定的字符串代表 NTAccount
还是 SecurityIdentifier
?
详细
给定一个字符串,我可以通过构造函数将其转换为 NTAccount
或 SecurityIdentifier
:
[string]$myAccount = '...' # some value
$ntAccount = [System.Security.Principal.NTAccount]::new($myAccount)
$sid = [System.Security.Principal.SecurityPrincipal]::new($myAccount) // throws exception if not a valid SID
如果字符串不是 SID,SecurityIdentifier 构造函数将抛出异常。
如果字符串是 SID,NTAccount 构造函数将接受它...但是,当我尝试将其转换为 SID 时,将抛出 System.Security.Principal.IdentityNotMapped
异常。
$sidFromNT = $ntAccount.Translate([System.Security.Principal.SecurityPrincipal]) # throw exception
$ntFromSid = $sid.Translate([System.Security.Principal.NTAccount]) # should work as if the SID were invalid we'd have already erred
如果我可以通过共享基数说我不知道类型就太好了class;但那是抽象的/没有 public 构造函数;所以我不能这样做:
[string]$account = '...'
$idRef = [System.Security.Principal.IdentityReference]::new($account) # this is not valid code
$ntFromId = $idRef.Translate([System.Security.Principal.NTAccount])
$sidFromId = $idRef.Translate([System.Security.Principal.SecurityIdentifier])
因此,我能想到的唯一选择是:
- 使用抛出的错误*判断类型;但这使错误成为我正常流程的一部分。
- 检查值是否以
S-
开头;非常快,在大多数情况下应该可以工作;但这是一个黑客。
* 注意:我意识到无效值(即既不是 NT 帐户也不是 SID)也会通过异常;为了保持简短,我暂时忽略了这种情况。
--
C#版本
由于这是一个 .net 问题而不是特定于 PowerShell 的问题,这里的代码在 C# 中说明了相同的问题(遗憾的是我不能 fiddle 这个,因为各种 Fiddle 站点限制了所需的功能)。
public static void Main()
{
var ids = new string[] {"S-1-1-0", "Everyone"}; // a list of values which may be SIDs or NTAccounts
var pseudoRandomIndex = DateTime.Now.Millisecond % ids.Length; // saves initialising Random for a simple demo
var idString = ids[pseudoRandomIndex]; // pick one id at random; be it a SID or an NT Account
Debug.WriteLine($"Selected value is {idString}");
TryToProcessIdentityReference<NTAccount, SecurityIdentifier>(idString, (id) => new NTAccount(id));
TryToProcessIdentityReference<SecurityIdentifier, NTAccount>(idString, (id) => new SecurityIdentifier(id));
}
static void TryToProcessIdentityReference<T1, T2>(string idString, Func<string, T1> callConstructor)
where T1 : IdentityReference
where T2 : IdentityReference
{
var t1Type = typeof(T1);
var t2Type = typeof(T2);
Console.WriteLine($"Trying to process {idString} as a {t1Type.Name} ...");
try
{
var t1 = callConstructor(idString);
_ = t1.Translate(t2Type);
Debug.WriteLine($" - {idString} is a valid {t1Type.Name}!");
} catch (Exception e) when(e is ArgumentException || e is IdentityNotMappedException) {
Debug.WriteLine($" - Failed to process {idString} as {t1Type.Name}; error thrown when translating to {t2Type.Name}");
Debug.WriteLine($" - {e.ToString()}");
}
}
也许组合 try..catch 块对您来说是可以接受的:
[string]$myAccount = '...' # some value
try {
# test if string $myAccount is an account name
if ($myAccount.Contains("\")) {
$domain, $user = $myAccount.Split("\", 2)
}
else {
$domain = $env:USERDOMAIN
$user = $myAccount
}
$ntAccount = [System.Security.Principal.NTAccount]::new($domain, $user)
$sid = [System.Security.Principal.NTAccount]::new($ntAccount).Translate([System.Security.Principal.SecurityIdentifier]).Value
# output something
"'$myAccount' is an account name:`r`nNT Account: $ntAccount`r`nSID: $sid"
}
catch {
# try if string $myAccount is a SID
if ($myAccount -like 'S-1-15-3*') {
Write-Warning "SID '$myAccount' is a Capability type SID and cannot be resolved."
}
else {
try {
$ntAccount = [System.Security.Principal.SecurityIdentifier]::new($myAccount).Translate([System.Security.Principal.NTAccount]).Value
"'$myAccount' is a SID:`r`nNT Account: $ntAccount`r`nSID: $myAccount"
}
catch {
Write-Warning "'$myAccount' could not be identified."
}
}
}
我意识到我可以使用 as
而不是构造函数来避免捕获异常。
PowerShell 版本
[string]$idString = @('Everyone', 'S-1-1-0') | Get-Random -Count 1
[System.Security.Principal.IdentityReference]$idRef = $idString -as [System.Security.Principal.SecurityIdentifier]
if ($null -eq $idRef) {
$idRef = $idString -as [System.Security.Principal.NTAccount]
}
"Input = $idString"
"SID = $($idRef.Translate([System.Security.Principal.SecurityIdentifier]).Value)"
"NTAccount = $($idRef.Translate([System.Security.Principal.NTAccount]).Value)"
C# 版本
注意:as
关键字在 C# 场景中无效,所以我上面的解决方案是特定于语言的...
我想出的最好办法是将错误流程片段封装在它自己的函数中,这样我们就可以在解决方案可用后轻松改进。
public static void Main()
{
var ids = new string[] {"S-1-1-0", "Everyone"}; // a list of values which may be SIDs or NTAccounts
var pseudoRandomIndex = DateTime.Now.Millisecond % ids.Length; // saves initialising Random for a simple demo
var idString = ids[pseudoRandomIndex]; // pick one id at random; be it a SID or an NT Account
var idRef = TryParse(idString, out var temp) ? (IdentityReference)temp : (IdentityReference)new NTAccount(idString);
Debug.WriteLine($"Selected value is {idString}");
Debug.WriteLine($"SID: {idRef.Translate(typeof(SecurityIdentifier))}");
Debug.WriteLine($"NTAccount: {idRef.Translate(typeof(NTAccount))}");
}
public static bool TryParse(string value, out SecurityIdentifier result)
{
try
{
result = new SecurityIdentifier(value);
return true;
}
catch (ArgumentException)
{
result = null;
return false;
}
}
问题
我如何可靠地判断给定的字符串代表 NTAccount
还是 SecurityIdentifier
?
详细
给定一个字符串,我可以通过构造函数将其转换为 NTAccount
或 SecurityIdentifier
:
[string]$myAccount = '...' # some value
$ntAccount = [System.Security.Principal.NTAccount]::new($myAccount)
$sid = [System.Security.Principal.SecurityPrincipal]::new($myAccount) // throws exception if not a valid SID
如果字符串不是 SID,SecurityIdentifier 构造函数将抛出异常。
如果字符串是 SID,NTAccount 构造函数将接受它...但是,当我尝试将其转换为 SID 时,将抛出 System.Security.Principal.IdentityNotMapped
异常。
$sidFromNT = $ntAccount.Translate([System.Security.Principal.SecurityPrincipal]) # throw exception
$ntFromSid = $sid.Translate([System.Security.Principal.NTAccount]) # should work as if the SID were invalid we'd have already erred
如果我可以通过共享基数说我不知道类型就太好了class;但那是抽象的/没有 public 构造函数;所以我不能这样做:
[string]$account = '...'
$idRef = [System.Security.Principal.IdentityReference]::new($account) # this is not valid code
$ntFromId = $idRef.Translate([System.Security.Principal.NTAccount])
$sidFromId = $idRef.Translate([System.Security.Principal.SecurityIdentifier])
因此,我能想到的唯一选择是:
- 使用抛出的错误*判断类型;但这使错误成为我正常流程的一部分。
- 检查值是否以
S-
开头;非常快,在大多数情况下应该可以工作;但这是一个黑客。
* 注意:我意识到无效值(即既不是 NT 帐户也不是 SID)也会通过异常;为了保持简短,我暂时忽略了这种情况。
--
C#版本
由于这是一个 .net 问题而不是特定于 PowerShell 的问题,这里的代码在 C# 中说明了相同的问题(遗憾的是我不能 fiddle 这个,因为各种 Fiddle 站点限制了所需的功能)。
public static void Main()
{
var ids = new string[] {"S-1-1-0", "Everyone"}; // a list of values which may be SIDs or NTAccounts
var pseudoRandomIndex = DateTime.Now.Millisecond % ids.Length; // saves initialising Random for a simple demo
var idString = ids[pseudoRandomIndex]; // pick one id at random; be it a SID or an NT Account
Debug.WriteLine($"Selected value is {idString}");
TryToProcessIdentityReference<NTAccount, SecurityIdentifier>(idString, (id) => new NTAccount(id));
TryToProcessIdentityReference<SecurityIdentifier, NTAccount>(idString, (id) => new SecurityIdentifier(id));
}
static void TryToProcessIdentityReference<T1, T2>(string idString, Func<string, T1> callConstructor)
where T1 : IdentityReference
where T2 : IdentityReference
{
var t1Type = typeof(T1);
var t2Type = typeof(T2);
Console.WriteLine($"Trying to process {idString} as a {t1Type.Name} ...");
try
{
var t1 = callConstructor(idString);
_ = t1.Translate(t2Type);
Debug.WriteLine($" - {idString} is a valid {t1Type.Name}!");
} catch (Exception e) when(e is ArgumentException || e is IdentityNotMappedException) {
Debug.WriteLine($" - Failed to process {idString} as {t1Type.Name}; error thrown when translating to {t2Type.Name}");
Debug.WriteLine($" - {e.ToString()}");
}
}
也许组合 try..catch 块对您来说是可以接受的:
[string]$myAccount = '...' # some value
try {
# test if string $myAccount is an account name
if ($myAccount.Contains("\")) {
$domain, $user = $myAccount.Split("\", 2)
}
else {
$domain = $env:USERDOMAIN
$user = $myAccount
}
$ntAccount = [System.Security.Principal.NTAccount]::new($domain, $user)
$sid = [System.Security.Principal.NTAccount]::new($ntAccount).Translate([System.Security.Principal.SecurityIdentifier]).Value
# output something
"'$myAccount' is an account name:`r`nNT Account: $ntAccount`r`nSID: $sid"
}
catch {
# try if string $myAccount is a SID
if ($myAccount -like 'S-1-15-3*') {
Write-Warning "SID '$myAccount' is a Capability type SID and cannot be resolved."
}
else {
try {
$ntAccount = [System.Security.Principal.SecurityIdentifier]::new($myAccount).Translate([System.Security.Principal.NTAccount]).Value
"'$myAccount' is a SID:`r`nNT Account: $ntAccount`r`nSID: $myAccount"
}
catch {
Write-Warning "'$myAccount' could not be identified."
}
}
}
我意识到我可以使用 as
而不是构造函数来避免捕获异常。
PowerShell 版本
[string]$idString = @('Everyone', 'S-1-1-0') | Get-Random -Count 1
[System.Security.Principal.IdentityReference]$idRef = $idString -as [System.Security.Principal.SecurityIdentifier]
if ($null -eq $idRef) {
$idRef = $idString -as [System.Security.Principal.NTAccount]
}
"Input = $idString"
"SID = $($idRef.Translate([System.Security.Principal.SecurityIdentifier]).Value)"
"NTAccount = $($idRef.Translate([System.Security.Principal.NTAccount]).Value)"
C# 版本
注意:as
关键字在 C# 场景中无效,所以我上面的解决方案是特定于语言的...
我想出的最好办法是将错误流程片段封装在它自己的函数中,这样我们就可以在解决方案可用后轻松改进。
public static void Main()
{
var ids = new string[] {"S-1-1-0", "Everyone"}; // a list of values which may be SIDs or NTAccounts
var pseudoRandomIndex = DateTime.Now.Millisecond % ids.Length; // saves initialising Random for a simple demo
var idString = ids[pseudoRandomIndex]; // pick one id at random; be it a SID or an NT Account
var idRef = TryParse(idString, out var temp) ? (IdentityReference)temp : (IdentityReference)new NTAccount(idString);
Debug.WriteLine($"Selected value is {idString}");
Debug.WriteLine($"SID: {idRef.Translate(typeof(SecurityIdentifier))}");
Debug.WriteLine($"NTAccount: {idRef.Translate(typeof(NTAccount))}");
}
public static bool TryParse(string value, out SecurityIdentifier result)
{
try
{
result = new SecurityIdentifier(value);
return true;
}
catch (ArgumentException)
{
result = null;
return false;
}
}