SecretManagement.KeeperPowerCommander.Extension/SecretManagement.KeeperPowerCommander.Extension.psm1
|
function Get-KeeperPowerCommanderMapPath { param([hashtable] $VaultParameters) if (-not $VaultParameters) { $VaultParameters = @{} } $mapPath = $VaultParameters.MapPath if (-not $mapPath) { $mapPath = Join-Path $env:USERPROFILE ".keeper-secret-map.json" } return $mapPath } function Get-KeeperPowerCommanderLookupMode { param([hashtable] $VaultParameters) if (-not $VaultParameters) { $VaultParameters = @{} } $mode = $VaultParameters.LookupMode if (-not $mode) { if ($VaultParameters.MapPath) { return "Map" } return "Map" } switch -Regex ([string] $mode) { '^(map|mapped)$' { return "Map" } '^(keepertitle|title|keeper)$' { return "KeeperTitle" } '^(hybrid|auto)$' { return "Hybrid" } } throw "Unsupported KeeperPowerCommander LookupMode '$mode'. Use 'Map', 'KeeperTitle', or 'Hybrid'." } function Get-KeeperPowerCommanderMapDocument { param([hashtable] $VaultParameters) $mapPath = Get-KeeperPowerCommanderMapPath -VaultParameters $VaultParameters if (-not (Test-Path -LiteralPath $mapPath)) { [pscustomobject]@{ secrets = @() } | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $mapPath -Encoding UTF8 } $raw = Get-Content -LiteralPath $mapPath -Raw if ([string]::IsNullOrWhiteSpace($raw)) { return [pscustomobject]@{ secrets = @() } } $map = $raw | ConvertFrom-Json if (-not ($map.PSObject.Properties.Name -contains "secrets")) { $map | Add-Member -NotePropertyName secrets -NotePropertyValue @() } return $map } function Save-KeeperPowerCommanderMapDocument { param( [object] $Map, [hashtable] $VaultParameters ) $mapPath = Get-KeeperPowerCommanderMapPath -VaultParameters $VaultParameters $parent = Split-Path -Parent $mapPath if ($parent -and -not (Test-Path -LiteralPath $parent)) { New-Item -ItemType Directory -Path $parent -Force | Out-Null } $tmpPath = "$mapPath.tmp" $Map | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $tmpPath -Encoding UTF8 Move-Item -LiteralPath $tmpPath -Destination $mapPath -Force } function Get-KeeperPowerCommanderMap { param([hashtable] $VaultParameters) $map = Get-KeeperPowerCommanderMapDocument -VaultParameters $VaultParameters return @($map.secrets) } function Find-KeeperPowerCommanderSecret { param( [string] $Name, [hashtable] $VaultParameters ) Get-KeeperPowerCommanderMap -VaultParameters $VaultParameters | Where-Object { $_.name -eq $Name } | Select-Object -First 1 } function ConvertTo-KeeperPowerCommanderSyntheticEntry { param( [object] $Record, [string] $Field ) $name = $Record.Name if (-not $name) { $name = $Record.Title } if (-not $name) { return $null } $uid = $Record.Uid if (-not $uid) { $uid = $Record.UID } if (-not $uid) { $uid = $Record.RecordUid } if (-not $uid) { return $null } [pscustomobject]@{ name = [string] $name uid = [string] $uid field = if ($Field) { [string] $Field } else { "Password" } source = "KeeperTitle" } } function Get-KeeperPowerCommanderRecordsByTitle { param([hashtable] $VaultParameters) Connect-KeeperPowerCommander -VaultParameters $VaultParameters @(Get-KeeperChildItem -ObjectType Record) } function Find-KeeperPowerCommanderSecretByTitle { param( [string] $Name, [hashtable] $VaultParameters ) $field = if ($VaultParameters -and $VaultParameters.DefaultField) { [string] $VaultParameters.DefaultField } else { "Password" } Get-KeeperPowerCommanderRecordsByTitle -VaultParameters $VaultParameters | Where-Object { $_.Name -eq $Name -or $_.Title -eq $Name } | ForEach-Object { ConvertTo-KeeperPowerCommanderSyntheticEntry -Record $_ -Field $field } | Where-Object { $null -ne $_ } | Select-Object -First 1 } function Find-KeeperPowerCommanderSecretEntry { param( [string] $Name, [hashtable] $VaultParameters ) $mode = Get-KeeperPowerCommanderLookupMode -VaultParameters $VaultParameters if ($mode -in @("Map", "Hybrid")) { $entry = Find-KeeperPowerCommanderSecret -Name $Name -VaultParameters $VaultParameters if ($entry) { return $entry } } if ($mode -in @("KeeperTitle", "Hybrid")) { return Find-KeeperPowerCommanderSecretByTitle -Name $Name -VaultParameters $VaultParameters } return $null } function Get-KeeperPowerCommanderFieldValue { param( [object] $Record, [string] $Field ) switch -Regex ($Field) { '^password$' { return Get-KeeperRecordPassword -Record $Record -Silent } '^login$' { return $Record.Login } '^url$' { if ($Record.Link) { return $Record.Link } return $Record.Url } '^notes$' { return $Record.Notes } } foreach ($collectionName in @("Custom", "Fields")) { $property = $Record.PSObject.Properties[$collectionName] if (-not $property) { continue } foreach ($item in @($property.Value)) { $label = $item.Label if (-not $label) { $label = $item.Name } if (-not $label) { $label = $item.TypeName } if ($label -and ($label -ieq $Field)) { $candidate = $item.Value if ($candidate -is [System.Array]) { $candidate = $candidate | Select-Object -First 1 } return [string] $candidate } } } return $null } function Connect-KeeperPowerCommander { param([hashtable] $VaultParameters) if (-not $VaultParameters) { $VaultParameters = @{} } if ($VaultParameters.SkipConnect) { return } Import-Module PowerCommander -ErrorAction Stop $connectParams = @{} if ($VaultParameters.Config) { $connectParams.Config = [string] $VaultParameters.Config } if ($VaultParameters.Server) { $connectParams.Server = [string] $VaultParameters.Server } if ($VaultParameters.SsoProvider) { $connectParams.SsoProvider = $true $connectParams.NewLogin = $true $enterpriseDomain = $VaultParameters.EnterpriseDomain if (-not $enterpriseDomain) { $enterpriseDomain = $VaultParameters.Username } if ($enterpriseDomain) { $connectParams.Username = [string] $enterpriseDomain } } elseif ($VaultParameters.NewLogin) { $connectParams.NewLogin = $true } elseif ($VaultParameters.Username) { $connectParams.Username = [string] $VaultParameters.Username } Connect-Keeper @connectParams | ForEach-Object { if ($_ -is [string]) { Write-Host $_ } } } function ConvertTo-KeeperPowerCommanderPlainText { param([object] $Secret) if ($Secret -is [securestring]) { return [System.Net.NetworkCredential]::new("", $Secret).Password } if ($Secret -is [pscredential]) { return $Secret.GetNetworkCredential().Password } if ($null -eq $Secret) { return "" } return [string] $Secret } function ConvertTo-KeeperPowerCommanderSecureString { param([object] $Secret) if ($Secret -is [securestring]) { return $Secret } ConvertTo-SecureString -String (ConvertTo-KeeperPowerCommanderPlainText -Secret $Secret) -AsPlainText -Force } function ConvertTo-KeeperPowerCommanderMapEntry { param( [string] $Name, [string] $Uid, [string] $Field, [hashtable] $Metadata ) $entry = [ordered]@{ name = $Name uid = $Uid field = $Field } if ($Metadata -and $Metadata.Description) { $entry.description = [string] $Metadata.Description } if ($Metadata) { $extra = [ordered]@{} foreach ($key in $Metadata.Keys) { if ($key -in @("Description", "Field", "KeeperUid", "Uid", "Folder", "FolderUid", "RecordType", "Title")) { continue } $extra[$key] = $Metadata[$key] } if ($extra.Count -gt 0) { $entry.metadata = $extra } } [pscustomobject] $entry } function Set-KeeperPowerCommanderMapEntry { param( [string] $Name, [string] $Uid, [string] $Field, [hashtable] $Metadata, [hashtable] $VaultParameters ) $map = Get-KeeperPowerCommanderMapDocument -VaultParameters $VaultParameters $entries = @($map.secrets) $replacement = ConvertTo-KeeperPowerCommanderMapEntry -Name $Name -Uid $Uid -Field $Field -Metadata $Metadata $found = $false $updated = foreach ($entry in $entries) { if ($entry.name -eq $Name) { $found = $true $replacement } else { $entry } } if (-not $found) { $updated = @($updated) + $replacement } $map.secrets = @($updated | Sort-Object name) Save-KeeperPowerCommanderMapDocument -Map $map -VaultParameters $VaultParameters } function Get-KeeperPowerCommanderRecordUidFromOutput { param([object[]] $Output) foreach ($item in @($Output)) { $text = [string] $item if ($text -match 'Record (?:created|updated):\s*(?<uid>\S+)') { return $Matches.uid } } return $null } function Set-Secret { param( [string] $VaultName, [string] $Name, [object] $Secret, [hashtable] $VaultParameters, [hashtable] $Metadata, [hashtable] $AdditionalParameters ) if (-not $VaultParameters -and $AdditionalParameters) { $VaultParameters = $AdditionalParameters } if (-not $Metadata) { $Metadata = @{} } $mode = Get-KeeperPowerCommanderLookupMode -VaultParameters $VaultParameters $entry = Find-KeeperPowerCommanderSecretEntry -Name $Name -VaultParameters $VaultParameters $field = if ($Metadata.Field) { [string] $Metadata.Field } elseif ($entry -and $entry.field) { [string] $entry.field } else { "Password" } $uid = if ($Metadata.KeeperUid) { [string] $Metadata.KeeperUid } elseif ($Metadata.Uid) { [string] $Metadata.Uid } elseif ($entry) { [string] $entry.uid } else { $null } $recordType = if ($Metadata.RecordType) { [string] $Metadata.RecordType } else { "login" } $title = if ($Metadata.Title) { [string] $Metadata.Title } else { $Name } $folder = if ($Metadata.Folder) { [string] $Metadata.Folder } elseif ($Metadata.FolderUid) { [string] $Metadata.FolderUid } elseif ($VaultParameters.Folder) { [string] $VaultParameters.Folder } elseif ($VaultParameters.FolderUid) { [string] $VaultParameters.FolderUid } else { $null } Connect-KeeperPowerCommander -VaultParameters $VaultParameters $secureSecret = ConvertTo-KeeperPowerCommanderSecureString -Secret $Secret $fieldArguments = @("-$field", $secureSecret) if ($uid) { $null = Add-KeeperRecord -Uid $uid -Title $title -Extra $fieldArguments 6>&1 } else { $createParams = @{ Title = $title RecordType = $recordType } if ($folder) { $createParams.Folder = $folder } $output = Add-KeeperRecord @createParams -Extra $fieldArguments 6>&1 $uid = Get-KeeperPowerCommanderRecordUidFromOutput -Output $output if (-not $uid) { $record = Get-KeeperChildItem -ObjectType Record | Where-Object Name -eq $title | Select-Object -First 1 if ($record) { $uid = $record.Uid } } } if (-not $uid) { throw "Keeper record was created or updated, but the record UID could not be determined for '$Name'." } if ($mode -ne "KeeperTitle") { Set-KeeperPowerCommanderMapEntry -Name $Name -Uid $uid -Field $field -Metadata $Metadata -VaultParameters $VaultParameters } return $true } function Set-SecretInfo { param( [string] $VaultName, [string] $Name, [hashtable] $Metadata, [hashtable] $VaultParameters, [hashtable] $AdditionalParameters ) if (-not $VaultParameters -and $AdditionalParameters) { $VaultParameters = $AdditionalParameters } if (-not $Metadata) { $Metadata = @{} } $entry = Find-KeeperPowerCommanderSecretEntry -Name $Name -VaultParameters $VaultParameters if (-not $entry) { throw "Secret '$Name' is not mapped in KeeperPowerCommander." } $uid = if ($Metadata.KeeperUid) { [string] $Metadata.KeeperUid } elseif ($Metadata.Uid) { [string] $Metadata.Uid } else { [string] $entry.uid } $field = if ($Metadata.Field) { [string] $Metadata.Field } elseif ($entry.field) { [string] $entry.field } else { "Password" } if (-not $Metadata.Description -and $entry.description) { $Metadata.Description = $entry.description } $mode = Get-KeeperPowerCommanderLookupMode -VaultParameters $VaultParameters if ($mode -eq "KeeperTitle") { throw "Set-SecretInfo cannot update Keeper metadata in LookupMode 'KeeperTitle'. Use Set-Secret with metadata or register with LookupMode 'Map' or 'Hybrid'." } Set-KeeperPowerCommanderMapEntry -Name $Name -Uid $uid -Field $field -Metadata $Metadata -VaultParameters $VaultParameters return $true } function Get-Secret { param( [string] $VaultName, [string] $Name, [hashtable] $VaultParameters, [hashtable] $AdditionalParameters ) if (-not $VaultParameters -and $AdditionalParameters) { $VaultParameters = $AdditionalParameters } $entry = Find-KeeperPowerCommanderSecretEntry -Name $Name -VaultParameters $VaultParameters if (-not $entry) { return $null } Connect-KeeperPowerCommander -VaultParameters $VaultParameters $record = Get-KeeperRecord -Uid $entry.uid -ErrorAction Stop $field = if ($entry.field) { $entry.field } else { "Password" } $value = Get-KeeperPowerCommanderFieldValue -Record $record -Field $field if ([string]::IsNullOrEmpty($value)) { return $null } ConvertTo-SecureString -String $value -AsPlainText -Force } function Remove-Secret { param( [string] $VaultName, [string] $Name, [hashtable] $VaultParameters, [hashtable] $AdditionalParameters ) throw "SecretManagement.KeeperPowerCommander is read-only. Remove or rotate Keeper records in Keeper." } function Get-SecretInfo { param( [string] $VaultName, [string] $Filter, [hashtable] $VaultParameters, [hashtable] $AdditionalParameters ) if (-not $VaultParameters -and $AdditionalParameters) { $VaultParameters = $AdditionalParameters } if ([string]::IsNullOrEmpty($Filter)) { $Filter = "*" } $mode = Get-KeeperPowerCommanderLookupMode -VaultParameters $VaultParameters if ($mode -eq "KeeperTitle") { $field = if ($VaultParameters.DefaultField) { [string] $VaultParameters.DefaultField } else { "Password" } $entries = Get-KeeperPowerCommanderRecordsByTitle -VaultParameters $VaultParameters | ForEach-Object { ConvertTo-KeeperPowerCommanderSyntheticEntry -Record $_ -Field $field } | Where-Object { $null -ne $_ -and $_.name -like $Filter } } elseif ($mode -eq "Hybrid") { $field = if ($VaultParameters.DefaultField) { [string] $VaultParameters.DefaultField } else { "Password" } $mapped = @(Get-KeeperPowerCommanderMap -VaultParameters $VaultParameters) $mappedNames = @($mapped | ForEach-Object { $_.name }) $discovered = Get-KeeperPowerCommanderRecordsByTitle -VaultParameters $VaultParameters | ForEach-Object { ConvertTo-KeeperPowerCommanderSyntheticEntry -Record $_ -Field $field } | Where-Object { $null -ne $_ -and $_.name -notin $mappedNames } $entries = @($mapped) + @($discovered) | Where-Object { $_.name -like $Filter } } else { $entries = Get-KeeperPowerCommanderMap -VaultParameters $VaultParameters | Where-Object { $_.name -like $Filter } } foreach ($entry in $entries) { $metadata = @{ KeeperUid = $entry.uid Field = if ($entry.field) { $entry.field } else { "Password" } } if ($entry.description) { $metadata.Description = $entry.description } [Microsoft.PowerShell.SecretManagement.SecretInformation]::new( [string] $entry.name, [Microsoft.PowerShell.SecretManagement.SecretType]::SecureString, $VaultName, $metadata ) } } function Unlock-SecretVault { param( [string] $VaultName, [hashtable] $VaultParameters, [hashtable] $AdditionalParameters ) if (-not $VaultParameters -and $AdditionalParameters) { $VaultParameters = $AdditionalParameters } Connect-KeeperPowerCommander -VaultParameters $VaultParameters return $true } function Test-SecretVault { param( [string] $VaultName, [hashtable] $VaultParameters, [hashtable] $AdditionalParameters ) try { if (-not $VaultParameters -and $AdditionalParameters) { $VaultParameters = $AdditionalParameters } $mode = Get-KeeperPowerCommanderLookupMode -VaultParameters $VaultParameters if ($mode -in @("Map", "Hybrid")) { $null = Get-KeeperPowerCommanderMap -VaultParameters $VaultParameters } Import-Module PowerCommander -ErrorAction Stop return $true } catch { Write-Error $_ return $false } } |