SecretManagement.PleasantPasswordServer.Extension/SecretManagement.PleasantPasswordServer.Extension.psm1

using namespace Microsoft.PowerShell.SecretManagement

function GetSecretFile
{
    $FilePath = Join-Path -Path $env:TEMP -ChildPath "PleasantCred.xml"

    if (Test-Path -Path $FilePath)
    {
        $Credential = Import-Clixml -Path $FilePath
        return $Credential
    }
    else
    {
        throw "Credential File not found. Please import the module and run New-PleasantCredential to create the file."
    }
}

function InvokeLoginToPleasant
{

    <#
        .SYNOPSIS
         Login to Pleasant Password Server

        .DESCRIPTION
         Login to Pleasant Password Server

        .PARAMETER AdditionalParameters
         The following values need to be in there:
           ServerURL
           Port
           Login as PSCredential Object

        .EXAMPLE
           $Password = ConvertTo-SecureString -String "xxx" -AsPlainText -Force
           $Cred = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "xxx", $Password

           $var = @{
              ServerURL = "https://ppsdc1.pps.net"
              Port = "10001"
              Login = $Cred
           }

           InvokeLoginToPleasant -AdditionalParameters $var

        .NOTES
           Author: Constantin Hager
           Date: 2020-12-31
    #>




    [CmdletBinding()]
    param (
        [Parameter()]
        [Hashtable]
        $AdditionalParameters
    )

    #$ServerURL = DecryptParameter -Parameter $AdditionalParameters.ServerURL
    #$Port = DecryptParameter -Parameter $AdditionalParameters.Port

    $PasswordServerURL = [string]::Concat($AdditionalParameters.ServerURL, ":", $AdditionalParameters.Port)

    $SecretFile = GetSecretFile

    # Create OAuth2 token params
    $tokenParams = @{
        grant_type = 'password';
        username   = $SecretFile.UserName;
        password   = $SecretFile.GetNetworkCredential().password;
    }

    # Authenticate to Pleasant Password Server
    $JSON = Invoke-WebRequest -Uri "$PasswordServerURL/OAuth2/Token" -Method POST -Body $tokenParams -ContentType "application/x-www-form-urlencoded" -ErrorAction SilentlyContinue

    if ($null -eq $JSON)
    {
        return $null
    }
    else
    {
        # Generate JSON token
        $Token = (ConvertFrom-Json $JSON.Content).access_token

        return $Token
    }

}

function Get-Secret
{
    param (
        [Parameter(Mandatory)]
        [string]
        $Name,

        [Parameter(Mandatory)]
        [string]
        $VaultName,

        [Parameter()]
        [hashtable]
        $AdditionalParameters
    )

    $Token = InvokeLoginToPleasant -AdditionalParameters $AdditionalParameters

    if ($null -eq $Token)
    {
        throw "No token received."
    }

    $headers = @{
        "Accept"        = "application/json"
        "Authorization" = "$Token"
    }

    $body = @{
        "search" = "$Name"
    }

    $PasswordServerURL = [string]::Concat($AdditionalParameters.ServerURL, ":", $AdditionalParameters.Port)

    $Secrets = Invoke-RestMethod -method post -Uri "$PasswordServerURL/api/v5/rest/search" -body (ConvertTo-Json $body) -Headers $headers -ContentType 'application/json'
    $id = $Secrets.Credentials.id

    if ($id.Count -gt 1)
    {
        throw "Multiple ambiguous entries found for $Name, please remove the duplicate entry"
    }

    if ($null -eq $id)
    {
        throw "No secret with $Name is found"
    }

    $Credential = Invoke-RestMethod -Method get -Uri "$PasswordServerURL/api/v5/rest/credential/$id" -Headers $headers -ContentType 'application/json'
    $Password = Invoke-RestMethod -method post -Uri "$PasswordServerURL/api/v5/rest/credential/$id/password" -body (ConvertTo-Json $body) -Headers $headers -ContentType 'application/json'

    $PasswordAsSecureString = ConvertTo-SecureString -String $Password -AsPlainText -Force
    return [PSCredential]::new($Credential.Username, $PasswordAsSecureString)
}

function Set-Secret
{
    param (

        [Parameter(Mandatory)]
        [string]
        $Name,

        [Parameter(Mandatory)]
        [object]
        $Secret,

        [Parameter(Mandatory)]
        [string]
        $VaultName,

        [Parameter(Mandatory)]
        [hashtable]
        $AdditionalParameters
    )

    $Token = InvokeLoginToPleasant -AdditionalParameters $AdditionalParameters
    $headers = @{
        "Accept"        = "application/json"
        "Authorization" = "$Token"
    }

    $PasswordServerURL = [string]::Concat($AdditionalParameters.ServerURL, ":", $AdditionalParameters.Port)

    $RootFolderid = Invoke-RestMethod -Uri "$PasswordServerURL/api/v5/rest/folders/root" -Headers $headers -ContentType 'application/json'

    $body_add = [ordered]@{
        "CustomUserFields"        = @{}
        "CustomApplicationFields" = @{}
        "Tags"                    = @()
        "Name"                    = $Name
        "UserName"                = $Secret.UserName
        "Password"                = $Secret.GetNetworkCredential().Password
        "Url"                     = ""
        "Notes"                   = ""
        "GroupId"                 = $RootFolderid
        "Expires"                 = $null
    }

    $splat = @{
        Uri             = "$PasswordServerURL/api/v5/rest/entries/"
        Method          = 'POST'
        Body            = (ConvertTo-Json $body_add)
        Headers         = $headers
        ContentType     = 'application/json'
        UseBasicParsing = $true
    }

    $Response = Invoke-WebRequest @splat

    if ($Response.StatusCode -eq 200)
    {
        return $true
    }
    else
    {
        return $false
    }
}

function Remove-Secret
{
    param (
        [Parameter(Mandatory)]
        [string]
        $Name,

        [Parameter(Mandatory)]
        [string]
        $VaultName,

        [Parameter(Mandatory)]
        [ValidateSet('Archive', 'Delete')]
        [string]
        $Action,

        [Parameter(Mandatory)]
        [string]
        $Comment,

        [Parameter()]
        [hashtable]
        $AdditionalParameters
    )

    $Token = InvokeLoginToPleasant -AdditionalParameters $AdditionalParameters
    $headers = @{
        "Accept"        = "application/json"
        "Authorization" = "$Token"
    }

    $body_search = @{
        "search" = "$Name"
    }

    $body_delete = [ordered]@{
        "Action"  = "$Action"
        "Comment" = "$Comment"
    }

    $PasswordServerURL = [string]::Concat($AdditionalParameters.ServerURL, ":", $AdditionalParameters.Port)

    $Secrets = Invoke-RestMethod -method post -Uri "$PasswordServerURL/api/v5/rest/search" -body (ConvertTo-Json $body_search) -Headers $headers -ContentType 'application/json'
    $id = $Secrets.Credentials.id

    if ($id.Count -gt 1)
    {
        throw "Multiple ambiguous entries found for $Name, please remove the duplicate entry"
    }

    if ($null -eq $id)
    {
        throw "No secret with $Name is found"
    }

    $splat = @{
        Uri             = "$PasswordServerURL/api/v5/rest/entries/$id"
        Method          = 'Delete'
        Body            = (ConvertTo-Json $body_delete)
        Headers         = $headers
        ContentType     = 'application/json'
        UseBasicParsing = $true
    }

    $Response = Invoke-WebRequest @splat

    if ($Response.StatusCode -eq 204)
    {
        return $true
    }
    else
    {
        return $false
    }
}

function Get-SecretInfo
{
    param (
        [Parameter(Mandatory)]
        [string]
        $VaultName,

        [Parameter()]
        [string]
        $Filter,

        [Parameter()]
        [hashtable]
        $AdditionalParameters
    )

    $Token = InvokeLoginToPleasant -AdditionalParameters $AdditionalParameters
    $headers = @{
        "Accept"        = "application/json"
        "Authorization" = "$Token"
    }

    $body = @{
        "search" = "$Filter"
    }

    $PasswordServerURL = [string]::Concat($AdditionalParameters.ServerURL, ":", $AdditionalParameters.Port)

    $Secrets = Invoke-RestMethod -method post -Uri "$PasswordServerURL/api/v5/rest/search" -body (ConvertTo-Json $body) -Headers $headers -ContentType 'application/json'
    $id = $Secrets.Credentials.id

    if ($id.Count -gt 1)
    {
        throw "Multiple ambiguous entries found for $Name, please remove the duplicate entry"
    }

    if ($null -eq $id)
    {
        throw "No secret with $Name is found"
    }

    return [Microsoft.PowerShell.SecretManagement.SecretInformation]::new(
        $Filter,
        [SecretType]::PSCredential,
        $VaultName
    )
}

function Test-SecretVault
{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory)]
        [string]
        $VaultName,

        [Parameter()]
        [hashtable]
        $AdditionalParameters
    )

    $Parameters = @{
        ServerURL = $AdditionalParameters.ServerURL
        Port      = $AdditionalParameters.Port
    }

    $Token = InvokeLoginToPleasant -AdditionalParameters $Parameters

    if ($null -eq $Token)
    {
        return $false
    }
    else
    {
        return $true
    }
}