Skip to content

Instantly share code, notes, and snippets.

@Jaykul
Created March 10, 2020 23:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Jaykul/81fd8ee55ce4df656d36d23e59788151 to your computer and use it in GitHub Desktop.
Save Jaykul/81fd8ee55ce4df656d36d23e59788151 to your computer and use it in GitHub Desktop.
Can anyone explain why a cast adapter for credential types is a bad idea?

Having run the CredentialConverter.ps1 I can now do things like this:

[PSCredential]$PSCredential = [Net.NetworkCredential]::new("Jaykul", "Secret", "HuddledMasses")

Ok, that's a bad example, please don't actually do that. But the point is that once I have a credential of one of these three supported (so far) types, I can just cast it to the other types. The only catch is that I implemented SqlCredential to try and preserve the Domain information (even though it's useless in SQL) so that I could cast it back to a NetworkCredential. That's a bit weird, but it means that for these three classes you can cast things without loosing any information!

[System.Data.SqlClient.SqlCredential]$SqlCredential = $PSCredential
[System.Net.NetworkCredential]$NetCredential = $SqlCredential
$NetCredential | ft -AutoSize

UserName Domain
-------- ------
Jaykul   HuddledMasses

Can someone explain to me why this is a bad idea?

class CredentialConverter : System.Management.Automation.PSTypeConverter
{
[bool] CanConvertFrom([PSObject]$psSourceValue, [Type]$destinationType)
{
Write-Debug "CanConvertFrom PSOBJECT $($psSourceValue.UserName) of type [$($psSourceValue.PSTypeNames -join '],[')] to $($destinationType.FullName)"
return $psSourceValue.PSTypeNames.Contains("System.Management.Automation.PSCredential") -or
$psSourceValue.PSTypeNames.Contains("System.Net.NetworkCredential") -or
$psSourceValue.PSTypeNames.Contains("System.Data.SqlClient.SqlCredential")
}
[bool] CanConvertFrom([object]$sourceValue, [Type]$destinationType)
{
$psSourceValue = ([PSObject]$sourceValue)
Write-Debug "CanConvertFrom OBJECT $($psSourceValue.UserName) of type [$($psSourceValue.PSTypeNames -join '],[')] to $($destinationType.FullName)"
return $psSourceValue.PSTypeNames.Contains("System.Management.Automation.PSCredential") -or
$psSourceValue.PSTypeNames.Contains("System.Net.NetworkCredential") -or
$psSourceValue.PSTypeNames.Contains("System.Data.SqlClient.SqlCredential") -and
$destinationType.FullName -in "System.Management.Automation.PSCredential", "System.Net.NetworkCredential", "System.Data.SqlClient.SqlCredential"
}
[bool] CanConvertTo([object]$sourceValue, [Type]$destinationType) {
$psSourceValue = ([PSObject]$sourceValue)
Write-Debug "CanConvertTo OBJECT $($psSourceValue.UserName) of type [$($psSourceValue.PSTypeNames -join '],[')] to $($destinationType.FullName)"
return $psSourceValue.PSTypeNames.Contains("System.Management.Automation.PSCredential") -or
$psSourceValue.PSTypeNames.Contains("System.Net.NetworkCredential") -or
$psSourceValue.PSTypeNames.Contains("System.Data.SqlClient.SqlCredential") -and
$destinationType.FullName -in "System.Management.Automation.PSCredential", "System.Net.NetworkCredential", "System.Data.SqlClient.SqlCredential"
}
[object] ConvertFrom([PSObject]$psSourceValue, [Type]$destinationType, [IFormatProvider]$formatProvider, [bool]$ignoreCase)
{
Write-Debug "ConvertFrom PSObject $($psSourceValue.UserName) of type [$($psSourceValue.PSTypeNames -join '],[')] to $($destinationType.FullName)"
if ($result = $psSourceValue -is $destinationType) {
return $result
}
if ($psSourceValue.Domain) {
$domain = $psSourceValue.Domain
$username = $psSourceValue.UserName
} else {
$domain, $username = $psSourceValue.UserName -split "\\"
if ($domain -and -not $username) {
$username, $domain = $domain, ""
}
}
# SqlCredential
if ($psSourceValue.UserId) {
$username = $psSourceValue.UserId
}
[SecureString]$password = if ($psSourceValue.SecurePassword) {
$psSourceValue.SecurePassword.Copy() # SqlCredential and NetworkCredential
} else {
$psSourceValue.Password.Copy() # PSCredential
}
switch ($destinationType.FullName) {
"System.Management.Automation.PSCredential" {
if ($domain) {
$username = $domain + "\" + $username
}
$result = [System.Management.Automation.PSCredential]::new($username, $password)
}
"System.Net.NetworkCredential" {
$result = [System.Net.NetworkCredential]::new($username, $password)
if ($domain) {
$result.Domain = $domain
}
}
"System.Data.SqlClient.SqlCredential" {
$null = $password.MakeReadOnly()
$result = [System.Data.SqlClient.SqlCredential]::new($userName, $password)
if ($domain) {
$result | Add-Member -NotePropertyName Domain -NotePropertyValue $domain
}
}
default {
throw [System.ArgumentException]::new("Can't convert $($psSourceValue) of type $($psSourceValue.GetType().Fullname) to $($destinationType.FullName)")
}
}
return $result
}
[object] ConvertFrom([object]$sourceValue, [Type]$destinationType, [IFormatProvider]$formatProvider, [bool]$ignoreCase)
{
$psSourceValue = ([PSObject]$sourceValue)
Write-Debug "ConvertFrom Object $($psSourceValue.UserName) of type [$($psSourceValue.PSTypeNames -join '],[')] to $($destinationType.FullName)"
return $this.ConvertFrom(([PSObject]$sourceValue), $destinationType, $formatProvider, $ignoreCase)
}
[object] ConvertTo([object]$sourceValue, [Type]$destinationType, [IFormatProvider]$formatProvider, [bool]$ignoreCase)
{
$psSourceValue = ([PSObject]$sourceValue)
Write-Debug "ConvertTo Object $($psSourceValue.UserName) of type [$($psSourceValue.PSTypeNames -join '],[')] to $($destinationType.FullName)"
return $this.ConvertFrom(([PSObject]$sourceValue), $destinationType, $formatProvider, $ignoreCase)
}
}
Update-TypeData -TypeName 'System.Management.Automation.PSCredential' -TypeConverter 'CredentialConverter'
Update-TypeData -TypeName 'System.Net.NetworkCredential' -TypeConverter 'CredentialConverter'
Update-TypeData -TypeName 'System.Data.SqlClient.SqlCredential' -TypeConverter 'CredentialConverter'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment