SecretManagement.KeePass.Extension/Tests/Get-Secret.Tests.ps1

Describe 'Get-Secret' {
    BeforeAll {
        Import-Module -Name 'Microsoft.PowerShell.SecretManagement'
        Import-Module -Name "$($PSScriptRoot)/../../SecretManagement.KeePass.psd1" -Force
        $SCRIPT:Mocks = Join-Path $PSScriptRoot 'Mocks'

        $ModuleName = 'SecretManagement.KeePass'
        $ModulePath = (Get-Module $ModuleName).Path
        $BaseKeepassDatabaseName = 'Testdb'
        $DoubleEntryExceptionMessage = 'Multiple ambiguous entries found for double entry, please remove the duplicate entry'
    }
    AfterAll {
        try {
            Microsoft.PowerShell.SecretManagement\Get-SecretVault -Name $VaultName -ErrorAction SilentlyContinue | Microsoft.PowerShell.SecretManagement\Unregister-SecretVault -ErrorAction SilentlyContinue
        } catch [system.Exception] { }
        Remove-Module 'SecretManagement.KeePass'
    }
    Context 'Function Parameter Validation' {
        BeforeAll {
            $SCRIPT:ExtModuleName = 'SecretManagement.KeePass.Extension'
            $SCRIPT:FunctionName = 'Get-Secret'
        }

        It 'has a parameter "<Name>"' {
            #TODO: Cut down on boilerplate after https://github.com/pester/Pester/issues/1603 is resolved
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{Name = $Name } {
                param($Name)
                $AllParameterNames = (Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters.Keys
                $Name | Should -BeIn $AllParameterNames
            }
        } -TestCases @(
            @{Name = 'Name' }
            @{Name = 'VaultName' }
            @{Name = 'AdditionalParameters' }
        )

        It 'has the mandatory value of parameter "<Name>" set to "<Mandatory>"' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{Name = $Name; Mandatory = $Mandatory } {
                param($Name,$Mandatory)
                $testAttribute = ((Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters[$Name].Attributes | 
                    Where-Object { $PSItem -is [System.Management.Automation.ParameterAttribute] }).Mandatory
                $testAttribute | Should -Be $Mandatory
            }
        } -TestCases @(
            @{Name = 'Name'; Mandatory = $False }
            @{Name = 'VaultName'; Mandatory = $False }
            @{Name = 'AdditionalParameters'; Mandatory = $False }
        ) 

        It 'has parameter <Name> of type <Type>' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{Name = $Name; Type = $Type } {
                param($Name,$Type)
                ((Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters[$Name].ParameterType) | Should -BeExactly $Type
            }
        } -TestCases @(
            @{Name = 'Name'; Type = 'string' }
            @{Name = 'VaultName'; Type = 'string' }
            @{Name = 'AdditionalParameters'; Type = 'hashtable' }
        )

        It 'has one parameter set' {
            InModuleScope 'SecretManagement.KeePass.Extension' {
                (Get-Command -Module $ExtModuleName -Name $FunctionName).ParameterSets.Count | Should -BeExactly 1
            }
        }
    }
    Context 'Get Secret information from MasterPassword protected KeePass' {
        BeforeAll {
            $MasterKey = '"1}`.2R{LX1`Jm8%XX2/'
            $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey))

            $VaultName = "KeepassPesterTest_$([guid]::NewGuid())"
            $KeePassDatabaseSuffix = 'PathOnly'
            $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx"
            $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName
            Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath

            $RegisterSecretVaultPathOnlyParams = @{
                Name            = $VaultName
                ModuleName      = $ModulePath
                PassThru        = $true
                VaultParameters = @{
                    Path = $VaultPath
                }
            }
            Microsoft.PowerShell.SecretManagement\Register-SecretVault @RegisterSecretVaultPathOnlyParams | Out-Null
            Mock -Verifiable -CommandName 'Get-Credential' -MockWith { $VaultMasterKey }
            Test-SecretVault -Name $VaultName | Out-Null
        }
        AfterAll {
            try {
                Microsoft.PowerShell.SecretManagement\Get-SecretVault -Name $VaultName -ErrorAction SilentlyContinue | Microsoft.PowerShell.SecretManagement\Unregister-SecretVault -ErrorAction SilentlyContinue
            } catch [system.Exception] { }
        }
        It 'should return a <PSType> for entry <SecretName>' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{SecretName = $SecretName; PSType = $PSType; VaultName = $VaultName } {
                param($SecretName,$PSType,$VaultName)
                $Secret = Get-Secret -Name $SecretName -VaultName $VaultName
                $Secret.Gettype().Fullname | Should -BeExactly $PSType
            }
        } -TestCases @(
            @{SecretName = 'New Entry 1';PSType = 'System.Management.Automation.PSCredential' }
            @{SecretName = 'New Entry 2';PSType = 'System.Management.Automation.PSCredential' }
            @{SecretName = 'No UserName';PSType = 'System.Security.SecureString' }
        )
 
        It 'should return <username> for <SecretName>' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{SecretName = $SecretName; UserName = $UserName; VaultName = $VaultName } {
                param($SecretName,$UserName,$VaultName)
                (Get-Secret -Name $SecretName -VaultName $VaultName).UserName | Should -BeExactly $UserName
            }
        } -TestCases @( 
            @{SecretName = 'New Entry 1';UserName = 'myusername 1' }
            @{SecretName = 'New Entry 2';UserName = 'Some Administrator account' }
        )

        It 'should throw when multiple secrets are returned' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{VaultName = $VaultName } {
                param($VaultName)
                { Get-Secret -Name 'double entry' -VaultName $VaultName 2>$null } | 
                    Should -Throw -ExpectedMessage $DoubleEntryExceptionMessage
            }
        }
        It 'should return nothing when entry is not found in the KeePass DB' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{VaultName = $VaultName } {
                param($VaultName)
                ( Get-Secret -Name 'not present' -VaultName $VaultName) | Should -BeNullOrEmpty
            }
        }
    }
    Context 'Get Secret information from KeyFile protected KeePass' {
        BeforeAll {
            $KeyFileName = 'TestdbKeyFile.key'
            $VaultName = "KeepassPesterTest_$([guid]::NewGuid())"
            $KeePassDatabaseSuffix = 'KeyFile'
            $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx"
            $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName
            $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName
            Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath
            Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath

            $RegisterSecretVaultPathOnlyParams = @{
                Name            = $VaultName
                ModuleName      = $ModulePath
                PassThru        = $true
                VaultParameters = @{
                    Path    = $VaultPath
                    KeyPath = $KeyPath
                }
            }
            Microsoft.PowerShell.SecretManagement\Register-SecretVault @RegisterSecretVaultPathOnlyParams | Out-Null

            Mock -Verifiable -CommandName 'Get-Credential' -MockWith { $VaultMasterKey }
            Test-SecretVault -Name $VaultName | Out-Null
        }
        AfterAll {
            try {
                Microsoft.PowerShell.SecretManagement\Get-SecretVault -Name $VaultName -ErrorAction SilentlyContinue | Microsoft.PowerShell.SecretManagement\Unregister-SecretVault -ErrorAction SilentlyContinue
            } catch [system.Exception] { }
        }
        It 'should return a <PSType> for entry <SecretName>' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ SecretName = $SecretName;PSType = $PSType;VaultName = $VaultName } {
                param($SecretName, $PSType, $VaultName)
                $Secret = Get-Secret -Name $SecretName -VaultName $VaultName
                $Secret | Should -Not -BeNullOrEmpty
                $Secret | Should -BeOfType $PSType
            }
        } -TestCases @(
            @{SecretName = 'New Entry 1';PSType = 'System.Management.Automation.PSCredential' }
            @{SecretName = 'New Entry 2';PSType = 'System.Management.Automation.PSCredential' }
            @{SecretName = 'No UserName';PSType = 'System.Security.SecureString' }
        )

        It 'should return <username> for <SecretName>' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ SecretName = $SecretName;UserName = $UserName;VaultName = $VaultName } {
                param($SecretName,$UserName,$VaultName)
                (Get-Secret -Name $SecretName -VaultName $VaultName).UserName | Should -BeExactly $UserName
            }
        } -TestCases @( 
            @{SecretName = 'New Entry 1';UserName = 'myusername 1' }
            @{SecretName = 'New Entry 2';UserName = 'Some Administrator account' }
        )

        It 'should throw when multiple secrets are returned' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ VaultName = $VaultName } {
                param($VaultName)
                { Get-Secret -Name 'double entry' -VaultName $VaultName 2>$null } | 
                    Should -Throw -ExpectedMessage $DoubleEntryExceptionMessage
            }
        }
        It 'should return nothing when entry is not found in the KeePass DB' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ VaultName = $VaultName } {
                param($VaultName)
                Get-Secret -Name 'not present' -VaultName $VaultName | Should -BeNullOrEmpty
            }
        }
    }
    Context 'Get Secret information from MasterPassword and KeyFile protected KeePass' {
        BeforeAll {
            $KeyFileName = 'TestdbKeyFileAndMasterPassword.key'
            $MasterKey = '"1}`.2R{LX1`Jm8%XX2/'
            $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey))

            $VaultName = "KeepassPesterTest_$([guid]::NewGuid())"
            $KeePassDatabaseSuffix = 'KeyFileAndMasterPassword'
            $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx"
            $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName
            $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName
            Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath
            Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath

            $RegisterSecretVaultPathOnlyParams = @{
                Name            = $VaultName
                ModuleName      = $ModulePath
                PassThru        = $true
                VaultParameters = @{
                    Path              = $VaultPath
                    UseMasterPassword = $true
                    KeyPath           = $KeyPath
                }
            }
            Microsoft.PowerShell.SecretManagement\Register-SecretVault @RegisterSecretVaultPathOnlyParams | Out-Null

            Mock -Verifiable -CommandName 'Get-Credential' -MockWith { $VaultMasterKey }
            Test-SecretVault -Name $VaultName | Out-Null
        }
        AfterAll {
            try {
                Microsoft.PowerShell.SecretManagement\Get-SecretVault -Name $VaultName -ErrorAction SilentlyContinue | Microsoft.PowerShell.SecretManagement\Unregister-SecretVault -ErrorAction SilentlyContinue
            } catch [system.Exception] { }
        }
        It 'should return a <PSType> for entry <SecretName>' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ SecretName = $SecretName;PSType = $PSType;VaultName = $VaultName } {
                param($SecretName, $PSType, $VaultName)
                $Secret = Get-Secret -Name $SecretName -VaultName $VaultName
                $Secret.Gettype().Fullname | Should -BeExactly $PSType
            }
        } -TestCases @(
            @{SecretName = 'New Entry 1';PSType = 'System.Management.Automation.PSCredential' }
            @{SecretName = 'New Entry 2';PSType = 'System.Management.Automation.PSCredential' }
            @{SecretName = 'No UserName';PSType = 'System.Security.SecureString' }
        )

        It 'should return <username> for <SecretName>' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ SecretName = $SecretName;UserName = $UserName;VaultName = $VaultName } {
                param($SecretName,$UserName,$VaultName)
                (Get-Secret -Name $SecretName -VaultName $VaultName).UserName | Should -BeExactly $UserName
            }
        } -TestCases @(
            @{SecretName = 'New Entry 1';UserName = 'myusername 1' }
            @{SecretName = 'New Entry 2';UserName = 'Some Administrator account' }
        )

        It 'should throw when multiple secrets are returned' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ VaultName = $VaultName } {
                param($VaultName)
                { Get-Secret -Name 'double entry' -VaultName $VaultName 2>$null } | 
                    Should -Throw -ExpectedMessage $DoubleEntryExceptionMessage
            }
        }
        It 'should return nothing when entry is not found in the KeePass DB' {
            InModuleScope 'SecretManagement.KeePass.Extension' -Parameters @{ VaultName = $VaultName } {
                param($VaultName)
                Get-Secret -Name 'not present' -VaultName $VaultName | Should -BeNullOrEmpty
            }
        }
    }
}