src/public/Security/Get-AitherCredential.ps1
|
#Requires -Version 7.0 <# .SYNOPSIS Retrieve a securely stored credential .DESCRIPTION Retrieves a credential previously stored with Set-AitherCredential. Decrypts and returns the credential in the requested format. This cmdlet is essential for: - Retrieving credentials for remote connections - Getting API keys for API calls - Accessing stored credentials in automation scripts - Avoiding hardcoded credentials .PARAMETER Name Name of the stored credential. This is the name used when storing the credential with Set-AitherCredential. .PARAMETER AsPlainText Return API key as plain text string instead of SecureString. WARNING: Use with caution as this exposes the credential in memory as plain text. Only use when absolutely necessary (e.g., for API headers that require plain text). .EXAMPLE $cred = Get-AitherCredential -Name "Production-SSH" New-AitherPSSession -ComputerName "server01" -Credential $cred Retrieves a stored credential and uses it to create a PSSession. .EXAMPLE $apiKey = Get-AitherCredential -Name "GitHub-Token" -AsPlainText $headers = @{ Authorization = "Bearer $apiKey" } Invoke-RestMethod -Uri "https://api.github.com/user" -Headers $headers Retrieves a GitHub token as plain text for use in API headers. .EXAMPLE $sshCred = Get-AitherCredential -Name "SSH-Server1" Test-AitherSSHConnection -Target "server.com" -Credential $sshCred Uses stored credential for SSH connection testing. .INPUTS System.String You can pipe credential names to Get-AitherCredential. .OUTPUTS PSCredential Returns PSCredential object for stored username/password credentials. SecureString Returns SecureString for stored API keys (default). System.String Returns plain text string for API keys when -AsPlainText is used. .NOTES Security: - Credentials are decrypted in memory only - Use SecureString format when possible - Avoid -AsPlainText unless absolutely necessary - Credentials are user-specific and cannot be accessed by other users - Credential files are encrypted and stored securely Error Handling: - Throws an error if credential is not found - Use Set-AitherCredential to store credentials before retrieving them .LINK Set-AitherCredential Remove-AitherCredential Get-AitherCredentialList #> function Get-AitherCredential { [OutputType([PSCredential], [SecureString], [System.String])] [CmdletBinding()] param( [Parameter(Mandatory=$false, Position = 0, ValueFromPipeline, ValueFromPipelineByPropertyName)] [ValidateNotNullOrEmpty()] [string]$Name, [Parameter()] [switch]$AsPlainText ) begin { $moduleRoot = Get-AitherModuleRoot # Determine credential storage path $credentialPath = if ($IsWindows) { Join-Path $env:USERPROFILE ".aitherzero" "credentials" } else { Join-Path $env:HOME ".aitherzero" "credentials" } } process { try { if ([string]::IsNullOrWhiteSpace($Name)) { # During module validation, Name may be empty - skip validation if ($PSCmdlet.MyInvocation.InvocationName -eq '.') { return } throw "Name parameter is required" } $credFile = Join-Path $credentialPath "$Name.cred" if (-not (Test-Path $credFile)) { $errorRecord = [System.Management.Automation.ErrorRecord]::new( [System.Exception]::new("Credential '$Name' not found. Use Set-AitherCredential to store it first."), "CredentialNotFound", [System.Management.Automation.ErrorCategory]::ObjectNotFound, $Name ) Invoke-AitherErrorHandler -ErrorRecord $errorRecord -Operation "Retrieving credential: $Name" -Parameters $PSBoundParameters -ThrowOnError return } try { $credData = Import-Clixml -Path $credFile if ($credData.Type -eq 'Credential') { # Return PSCredential if ($credData.Password) { # Check if it's already a SecureString or needs conversion if ($credData.Password -is [SecureString]) { $password = $credData.Password } elseif ($credData.Password -is [string] -and $credData.Password.Length -gt 0) { $password = $credData.Password | ConvertTo-SecureString -AsPlainText } else { throw "Credential password is empty or invalid" } return [PSCredential]::new($credData.Username, $password) } else { throw "Credential password is empty or invalid" } } elseif ($credData.Type -eq 'ApiKey') { # Return API Key if ($credData.Key) { if ($credData.Key -is [SecureString]) { $secureKey = $credData.Key } elseif ($credData.Key -is [string] -and $credData.Key.Length -gt 0) { $secureKey = $credData.Key | ConvertTo-SecureString -AsPlainText } else { throw "API key is empty or invalid" } } else { throw "API key is empty or invalid" } if ($AsPlainText) { # Use helper function for secure conversion if (Get-Command ConvertFrom-SecureStringSecurely -ErrorAction SilentlyContinue) { return ConvertFrom-SecureStringSecurely -SecureString $secureKey } else { # Fallback - less secure but functional $bstr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secureKey) try { return [Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } } else { return $secureKey } } else { $errorRecord = [System.Management.Automation.ErrorRecord]::new( [System.Exception]::new("Unknown credential type: $($credData.Type)"), "InvalidCredentialType", [System.Management.Automation.ErrorCategory]::InvalidData, $credData.Type ) Invoke-AitherErrorHandler -ErrorRecord $errorRecord -Operation "Retrieving credential: $Name" -Parameters $PSBoundParameters -ThrowOnError } } catch { Invoke-AitherErrorHandler -ErrorRecord $_ -Operation "Decrypting credential: $Name" -Parameters $PSBoundParameters -ThrowOnError } } catch { Invoke-AitherErrorHandler -ErrorRecord $_ -Operation "Retrieving credential: $Name" -Parameters $PSBoundParameters -ThrowOnError } } } |