SecretManagement.LastPass.Extension/SecretManagement.LastPass.Extension.psm1

# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

function Invoke-lpass {
    [CmdletBinding()]
    param (
        [Parameter()]
        [string[]]
        $Arguments,

        [Parameter(ValueFromPipeline)]
        [object]
        $InputObject
    )

    if (Get-Command lpass -ErrorAction SilentlyContinue) {
        if ($InputObject) {
            return $InputObject | & lpass @Arguments
        }
        return lpass @Arguments
    }

    throw "lpass executable not found or installed."
}

function Get-Secret
{
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $Name,
        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $VaultName,
        [Parameter(ValueFromPipelineByPropertyName)]
        [hashtable] $AdditionalParameters
    )

    # TODO error handling

    if ($Name -match ".* \[id: (\d*)\]") {
        $Name = $Matches[1]
    }

    $res = Invoke-lpass 'show','--name', $Name, '--password'
    if ([string]::IsNullOrWhiteSpace($res)) {
        $res = Invoke-lpass 'show', '--name', $Name, '--notes'
    }

    return $res
}

function Set-Secret
{
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $Name,
        [Parameter(ValueFromPipelineByPropertyName)]
        [object] $Secret,
        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $VaultName,
        [Parameter(ValueFromPipelineByPropertyName)]
        [hashtable] $AdditionalParameters
    )
    if($Secret -is [string]) {
        $Secret = @{
            URL = "http://sn"
            Notes = $Secret
        }
    }

    $sb = [System.Text.StringBuilder]::new()
    if($Secret.UserName) {
        $sb.Append("Username: ").AppendLine($Secret.UserName)
        $sb.Append("login: ").AppendLine($Secret.UserName)
    }

    if($Secret.Password) {
        $pass = $Secret.Password
        if ($Secret -is [pscredential]) {
            $pass = $Secret.GetNetworkCredential().password
        } elseif ($pass -is [securestring]) {
            $pass = $pass | ConvertFrom-SecureString
        }

        $sb.Append("Password: ").AppendLine($pass)
        $sb.Append("password: ").AppendLine($pass)
        $sb.Append("sudo_password: ").AppendLine($pass)
    }

    if($Secret.URL) {
        $sb.Append("URL: ").AppendLine($Secret.URL)
    }

    if($Secret.Notes) {
        $sb.AppendLine("Notes:").AppendLine($Secret.Notes)
    }

    try {
        $sb.ToString() | Invoke-lpass 'add', $Name, '--non-interactive'
    } catch {
        return $false
    }
    return $true
}

function Remove-Secret
{
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $Name,
        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $VaultName,
        [Parameter(ValueFromPipelineByPropertyName)]
        [hashtable] $AdditionalParameters
    )

    if ($Name -match ".* \[id: (\d*)\]") {
        $Name = $Matches[1]
    }

    lpass rm $Name
    return $?
}

function Get-SecretInfo
{
    param (
        [string] $Filter,
        [string] $VaultName,
        [hashtable] $AdditionalParameters
    )

    $Filter = "*$Filter"
    $pattern = [WildcardPattern]::new($Filter)
    Invoke-lpass 'ls' |
        Where-Object { 
            $_ -match "(.*) \[id: \d*\]" | Out-Null
            $pattern.IsMatch($Matches[1])
        } |
        ForEach-Object {
            [Microsoft.PowerShell.SecretManagement.SecretInformation]::new(
                $_,
                [Microsoft.PowerShell.SecretManagement.SecretType]::SecureString,
                $VaultName)
        }
}

function Test-SecretVault
{
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [string] $VaultName,
        [Parameter(ValueFromPipelineByPropertyName)]
        [hashtable] $AdditionalParameters
    )
    return (Get-Command lpass -ErrorAction SilentlyContinue) -and (lpass status -match "Logged in as .*")
}