├── .config ├── RequiredModules.psd1 └── dotnet-tools.json ├── .devcontainer ├── Dockerfile └── devcontainer.json ├── .github └── workflows │ └── press.yml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── PSModule.build.ps1 ├── README.MD ├── SecretManagement.KeePass ├── PoshKeePass │ ├── PoShKeePass.format.ps1xml │ ├── PoShKeePass.psd1 │ ├── PoShKeePass.psm1 │ ├── bin │ │ ├── PTLibRestore.dll │ │ ├── SkiaSharp.dll │ │ ├── System.Buffers.dll │ │ ├── System.Memory.dll │ │ ├── System.Numerics.Vectors.dll │ │ ├── System.Runtime.CompilerServices.Unsafe.dll │ │ ├── System.Security.Cryptography.ProtectedData.dll │ │ └── pt.KeePassLibStd.dll │ ├── changelog.md │ ├── license │ └── readme.md ├── Public │ ├── Register-KeePassSecretVault.ps1 │ └── Unlock-KeePassSecretVault.ps1 ├── SecretManagement.KeePass.Extension │ ├── Private │ │ ├── ConvertTo-ReadOnlyDictionary.ps1 │ │ ├── GetKeepassParams.ps1 │ │ ├── Test-DBChanged.ps1 │ │ ├── Test-Verbose.ps1 │ │ ├── Unlock-SecureString.ps1 │ │ └── VaultError.ps1 │ ├── Public │ │ ├── Connect-KeepassDatabase.ps1 │ │ ├── Get-Secret.ps1 │ │ ├── Get-SecretInfo.ps1 │ │ ├── Remove-Secret.ps1 │ │ ├── Set-Secret.ps1 │ │ ├── Test-SecretVault.ps1 │ │ ├── Unlock-SecretVault.ps1 │ │ └── Unregister-SecretVault.ps1 │ ├── SecretManagement.KeePass.Extension.psd1 │ └── SecretManagement.KeePass.Extension.psm1 ├── SecretManagement.KeePass.psd1 ├── SecretManagement.KeePass.psm1 └── Tests │ ├── Get-Secret.Tests.ps1 │ ├── Mocks │ ├── TestdbKeyFile.kdbx │ ├── TestdbKeyFile.key │ ├── TestdbKeyFileAndMasterPassword.kdbx │ ├── TestdbKeyFileAndMasterPassword.key │ ├── TestdbKeyFileV2.kdbx │ ├── TestdbKeyFileV2.keyx │ ├── TestdbPathAndUseMasterPassword.kdbx │ └── TestdbPathOnly.kdbx │ ├── Register-KeePassSecretVault.Tests.ps1 │ ├── Remove-Secret.Tests.ps1 │ ├── SecretManagementVault.Tests.ps1 │ ├── Test-SecretVault.Tests.ps1 │ └── TestSecretVault-CommonTests.include.ps1 ├── build.ps1 └── images ├── KeePassSecretManagementDemo.gif └── Logo.png /.config/RequiredModules.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | 'Microsoft.Powershell.SecretManagement' = '1.1.0' 3 | 'PowerConfig' = '0.1.3' 4 | 'PSFramework' = '1.6.205' 5 | } 6 | -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "gitversion.tool": { 6 | "version": "5.6.6", 7 | "commands": [ 8 | "dotnet-gitversion" 9 | ] 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/powershell:ubuntu-18.04 2 | RUN pwsh -noprofile -noninteractive -c 'Install-Module PowershellGet -Scope AllUsers -AllowPrerelease -Force' 3 | RUN pwsh -noprofile -noninteractive -c 'Register-PSResourceRepository -PSGallery -Trusted;Install-PSResource Pester -Scope AllUsers -Verbose;Install-PSResource -Scope AllUsers Microsoft.Powershell.SecretManagement' 4 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.154.2/containers/powershell 3 | { 4 | "name": "PowerShell", 5 | "build": { 6 | "dockerfile": "Dockerfile" 7 | }, 8 | 9 | // Set *default* container specific settings.json values on container create. 10 | "settings": { 11 | "terminal.integrated.shell.linux": "/usr/bin/pwsh" 12 | }, 13 | 14 | // Add the IDs of extensions you want installed when the container is created. 15 | "extensions": [ 16 | "ms-vscode.powershell-preview", 17 | "hbenl.vscode-test-explorer", 18 | "mhutchie.git-graph", 19 | "cschleiden.vscode-github-actions", 20 | "tylerleonhardt.vscode-pester-test-adapter" 21 | ], 22 | 23 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 24 | // "forwardPorts": [], 25 | 26 | // Uncomment the next line to run commands after the container is created. This gets run in bash which is why we call `pwsh`. 27 | //"postCreateCommand": "/usr/bin/pwsh -c 'Install-Module Pester,Microsoft.Powershell.SecretManagement -Force'", 28 | 29 | // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 30 | // "remoteUser": "vscode" 31 | } 32 | -------------------------------------------------------------------------------- /.github/workflows/press.yml: -------------------------------------------------------------------------------- 1 | name: Press Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - main 8 | - ci 9 | - production 10 | tags: 11 | - '*' 12 | release: 13 | types: 14 | - published 15 | pull_request: 16 | branches: 17 | - master 18 | - main 19 | - production 20 | jobs: 21 | build: 22 | name: 👷 Build 23 | runs-on: ubuntu-20.04 24 | steps: 25 | # Workaround: https://github.com/actions/runner/issues/520#issuecomment-720508121 26 | - if: always() 27 | name: 🐛 Get Debug Status 28 | id: debugStatus 29 | run: | 30 | echo '::set-output name=runnerDebug::${{secrets.ACTIONS_RUNNER_DEBUG}}' 31 | echo '::set-output name=stepDebug::${{secrets.ACTIONS_STEP_DEBUG}}' 32 | 33 | - name: 🚚 Checkout 34 | uses: actions/checkout@v2 35 | with: 36 | # Required for GitVersion 37 | #TODO: Calculate fetch depth up to the parent branch's last version tag (this is all gitversion really needs) 38 | fetch-depth: 0 39 | - name: 🔗 Cache Powershell Modules 40 | uses: actions/cache@v2 41 | with: 42 | path: | 43 | ~/Documents/Powershell/Modules 44 | ~/Documents/WindowsPowershell/Modules 45 | ~/.local/share/powershell/Modules 46 | ~/.local/share/Press 47 | ~/AppData/Local/Press 48 | ~/.nuget/packages/gitversion.tool 49 | key: build-${{ hashFiles('.config/RequiredModules.psd1') }} 50 | 51 | - if: steps.debugStatus.outputs.stepDebug 52 | name: 🔬 Powershell Environment Information 53 | shell: pwsh 54 | run: | 55 | "::group::Powershell Modules" 56 | ($ENV:PSModulePath).split([io.path]::pathseparator) | where {Test-Path $_} | Get-ChildItem 57 | "::endgroup" 58 | 59 | "::group::Variables" 60 | Get-Variable | select name,value | Out-String 61 | "::endgroup" 62 | 63 | "::group::Environment" 64 | dir env: | Out-String 65 | "::endgroup" 66 | 67 | - name: 👷 Build 68 | id: build 69 | shell: pwsh 70 | run: | 71 | if ('${{secrets.ACTIONS_STEP_DEBUG}}') {$verbosePreference = 'continue'} 72 | ./build '.,Package' 73 | 74 | - name: 📦 Capture Powershell Module 75 | uses: actions/upload-artifact@v2 76 | with: 77 | name: PSModule 78 | path: BuildOutput/${{ steps.build.outputs.moduleName }} 79 | 80 | - name: 📦 Capture Powershell Zip 81 | uses: actions/upload-artifact@v2 82 | with: 83 | name: ${{ steps.build.outputs.moduleName }}-${{ steps.build.outputs.nugetVersion }}.zip 84 | #FIXME: Double zip 85 | path: BuildOutput/*.zip 86 | 87 | - name: 📦 Capture Powershell Nuget Package 88 | uses: actions/upload-artifact@v2 89 | with: 90 | name: ${{ steps.build.outputs.moduleName }}.${{ steps.build.outputs.nugetVersion }}.nupkg 91 | path: BuildOutput/*.nupkg 92 | 93 | - if: always() && runner.os != 'Windows' && steps.debugStatus.outputs.runnerDebug 94 | name: 🐛 Debug via SSH if ACTIONS_RUNNER_DEBUG secret is set 95 | uses: lhotari/action-upterm@v1 96 | 97 | #TODO: Move to dedicated function 98 | - name: 📦 Update Draft Github Release 99 | if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.ref == 'refs/heads/ci' 100 | shell: pwsh 101 | run: | 102 | ./build 'Press.UpdateGitHubRelease' 103 | env: 104 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 105 | 106 | test: 107 | name: 🧪 Test 108 | needs: build 109 | runs-on: ${{ matrix.os }} 110 | strategy: 111 | fail-fast: true 112 | matrix: 113 | os: 114 | # - ubuntu-latest 115 | - ubuntu-20.04 116 | - ubuntu-18.04 117 | # - ubuntu-16.04 118 | # - windows-latest 119 | - windows-2019 120 | # - windows-2016 121 | # - macos-latest 122 | - macos-11.0 123 | - macos-10.15 124 | steps: 125 | - name: 🚚 Checkout 126 | uses: actions/checkout@v2 127 | with: 128 | # Required for GitVersion 129 | #TODO: Calculate fetch depth up to the parent branch's last version tag (this is all gitversion really needs) 130 | fetch-depth: 0 131 | 132 | - name: 🔗 Cache Powershell Modules 133 | uses: actions/cache@v2 134 | with: 135 | path: | 136 | ~/Documents/Powershell/Modules 137 | ~/Documents/WindowsPowershell/Modules 138 | ~/.local/share/powershell/Modules 139 | ~/.local/share/Press 140 | ~/AppData/Local/Press 141 | ~/.nuget/packages/gitversion.tool 142 | key: test-${{ matrix.os }}-${{ hashFiles('Source/.config/RequiredModules.psd1') }} 143 | 144 | - name: ➕ Restore Built Powershell Module 145 | uses: actions/download-artifact@v2 146 | with: 147 | name: PSModule 148 | #TODO: Pull this from environment setup 149 | path: BuildOutput/${{ github.event.repository.name }} 150 | 151 | - name: 🧪 Test Powershell 7+ 152 | shell: pwsh 153 | run: | 154 | if ('${{secrets.ACTIONS_STEP_DEBUG}}') {$verbosePreference = 'continue'} 155 | #Press Meta 156 | if ('${{ github.event.repository.name }}' -eq 'Press') { 157 | $GLOBAL:PressModulePath = Resolve-Path ./BuildOutput/Press/Press.psd1 158 | } 159 | 160 | . ./build.ps1 'Test' 161 | 162 | deployPrerelease: 163 | name: 🚀 Deploy Prelease Module 164 | if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' 165 | runs-on: ubuntu-20.04 166 | environment: Powershell Gallery PreRelease 167 | needs: 168 | - build 169 | - test 170 | steps: 171 | - name: ➕ Restore Built Powershell Module 172 | uses: actions/download-artifact@v2 173 | with: 174 | name: PSModule 175 | #TODO: Get BuildOutput Config Path 176 | path: BuildOutput/${{ github.event.repository.name }} 177 | - name: 🚀 Publish Module to PowerShell Gallery 178 | shell: pwsh 179 | run: | 180 | if (-not '${{ secrets.PS_GALLERY_KEY }}') {throw 'You need to configure a PS_GALLERY_KEY secret for this environment with your Powershell Gallery API Key'} 181 | Install-Module Microsoft.Powershell.SecretManagement -RequiredVersion 1.1.0 -Force 182 | Install-Module PSFramework -RequiredVersion 1.6.205 -Force -AllowClobber 183 | Publish-Module -Verbose -Name $PWD/BuildOutput/${{ github.event.repository.name }} -NugetApiKey ${{ secrets.PS_GALLERY_KEY }} 184 | 185 | deploy: 186 | name: 🚀 Deploy 187 | if: startsWith(github.ref, 'refs/tags/') 188 | runs-on: ubuntu-20.04 189 | environment: Powershell Gallery 190 | needs: 191 | - build 192 | - test 193 | steps: 194 | - name: ➕ Restore Built Powershell Module 195 | uses: actions/download-artifact@v2 196 | with: 197 | name: PSModule 198 | #TODO: Get BuildOutput Config Path 199 | path: BuildOutput/${{ github.event.repository.name }} 200 | - name: 🚀 Publish Module to PowerShell Gallery 201 | shell: pwsh 202 | run: | 203 | if (-not '${{ secrets.PS_GALLERY_KEY }}') {throw 'You need to configure a PS_GALLERY_KEY secret for this environment with your Powershell Gallery API Key'} 204 | Install-Module Microsoft.Powershell.SecretManagement -RequiredVersion 1.1.0 -Force 205 | Install-Module PSFramework -RequiredVersion 1.6.205 -Force -AllowClobber 206 | Publish-Module -Verbose -Name $PWD/BuildOutput/${{ github.event.repository.name }} -NugetApiKey ${{ secrets.PS_GALLERY_KEY }} 207 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | TestExplorerResults.xml 2 | BuildOutput -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "github-actions.workflows.pinned.refresh.enabled": true, 3 | "github-actions.workflows.pinned.refresh.interval": 5, 4 | "editor.formatOnSaveMode": "modifications", 5 | "editor.formatOnSave": true, 6 | "github-actions.workflows.pinned.workflows": [ 7 | ".github/workflows/press.yml" 8 | ] 9 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Justin Grote 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /PSModule.build.ps1: -------------------------------------------------------------------------------- 1 | 2 | if (-not (Get-Module PowerConfig -ErrorAction SilentlyContinue)) { 3 | try { 4 | Import-Module PowerConfig -ErrorAction Stop 5 | } catch { 6 | Install-Module PowerConfig -AllowPrerelease -Force 7 | Import-Module PowerConfig -ErrorAction Stop 8 | } 9 | } 10 | if (-not (Get-Module Press -ErrorAction SilentlyContinue)) { 11 | try { 12 | Import-Module Press -ErrorAction Stop 13 | } catch { 14 | Install-Module Press -Force 15 | Import-Module Press -ErrorAction Stop 16 | } 17 | } 18 | if (-not (Get-Module 'Microsoft.Powershell.SecretManagement' -ErrorAction SilentlyContinue)) { 19 | try { 20 | Import-Module 'Microsoft.Powershell.SecretManagement' -ErrorAction Stop 21 | } catch { 22 | Install-Module 'Microsoft.Powershell.SecretManagement' -AllowPrerelease -RequiredVersion '1.1.0' -Force 23 | Import-Module 'Microsoft.Powershell.SecretManagement' -ErrorAction Stop 24 | } 25 | } 26 | if (-not (Get-Module 'PSFramework' -ErrorAction SilentlyContinue)) { 27 | try { 28 | Import-Module 'PSFramework' -ErrorAction Stop 29 | } catch { 30 | Install-Module 'PSFramework' -AllowPrerelease -RequiredVersion '1.6.205' -Force -AllowClobber 31 | Import-Module 'PSFramework' -ErrorAction Stop 32 | } 33 | } 34 | 35 | . Press.Tasks 36 | 37 | Task Press.CopyModuleFiles @{ 38 | Inputs = { 39 | Get-ChildItem -File -Recurse $PressSetting.General.SrcRootDir 40 | $SCRIPT:IncludeFiles = ( 41 | (Get-ChildItem -File -Recurse "$($PressSetting.General.SrcRootDir)\SecretManagement.KeePass.Extension") | 42 | Resolve-Path 43 | ) 44 | $IncludeFiles 45 | } 46 | Outputs = { 47 | $buildItems = Get-ChildItem -File -Recurse $PressSetting.Build.ModuleOutDir 48 | if ($buildItems) { $buildItems } else { 'EmptyBuildOutputFolder' } 49 | } 50 | Jobs = { 51 | Remove-BuildItem $PressSetting.Build.ModuleOutDir 52 | 53 | $copyResult = Copy-PressModuleFiles @commonParams ` 54 | -Destination $PressSetting.Build.ModuleOutDir ` 55 | -PSModuleManifest $PressSetting.BuildEnvironment.PSModuleManifest 56 | 57 | $PressSetting.OutputModuleManifest = $copyResult.OutputModuleManifest 58 | } 59 | } 60 | 61 | Task CopyKeePassExtension -After Press.CopyModuleFiles { 62 | #KeePass Extension Files 63 | $KPExtensionPath = "$($PressSetting.General.SrcRootDir)\SecretManagement.KeePass.Extension" 64 | Copy-Item $KPExtensionPath -Recurse -Force -Exclude '*.Tests.ps1' -Destination $PressSetting.Build.ModuleOutDir -Container 65 | } 66 | 67 | Task CopyPoshKeePass -After Press.CopyModuleFiles { 68 | #KeePass Extension Files 69 | $PKPExtensionPath = "$($PressSetting.General.SrcRootDir)\PoshKeePass" 70 | Copy-Item $PKPExtensionPath -Recurse -Force -Exclude '*.Tests.ps1' -Destination $PressSetting.Build.ModuleOutDir -Container 71 | } 72 | 73 | Task Package Press.Package.Zip 74 | 75 | Task Press.Test.Pester.WindowsPowershell { 76 | Write-Warning 'Windows Powershell Tests cannot currently be run due to a bug. Run the tests manually. Remove when https://github.com/pester/Pester/issues/1974 is closed' 77 | } 78 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # KeePass Secret Management Extension 2 | 3 | ## Actions 4 | 5 | [![Actions][]][ActionsLink] 6 | 7 | ## Summary 8 | 9 | This is a simple proof-of-concept using a modified version of PoshKeePass to allow for cross platform usage. 10 | 11 | ![KeePass SecretManagement Demo](./images/KeePassSecretManagementDemo.gif) 12 | 13 | ## Quick Start 14 | 15 | 1. Create a keepass database with a master password, Keyfile or both. 16 | 1. `Install-Module SecretManagement.KeePass` 17 | 1. Run the following command: 18 | 19 | ```powershell 20 | Register-SecretVault -Name 'testVault' -ModuleName 'SecretManagement.Keepass' -VaultParameters @{ 21 | Path = "path/to/my/vault.kdbx" 22 | UseMasterPassword = $true 23 | KeyPath= "path/to/my/keyfile.key" 24 | } 25 | ``` 26 | 27 | 1. (optional) Use Test-SecretVault to validate your connection to the vault 28 | 29 | ```powershell 30 | Test-SecretVault -Name 'testVault' 31 | ``` 32 | 33 | 2. Get the secret data using Get-Secret 34 | 35 | ```powershell 36 | Get-Secret -Name 'My secret entry 1' -Vault 'testVault' 37 | ``` 38 | 39 | ## Known Limitations 40 | 41 | [Actions]: https://github.com/JustinGrote/SecretManagement.KeePass/workflows/Test/badge.svg?branch=main 42 | [ActionsLink]: https://github.com/JustinGrote/SecretManagement.KeePass/workflows 43 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/PoShKeePass.format.ps1xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PSKeePass.Entry 6 | 7 | PSKeePass.Entry 8 | 9 | 10 | 11 | 12 | 13 | 14 | Title 15 | 16 | 17 | UserName 18 | 19 | 20 | Password 21 | 22 | 23 | FullPath 24 | 25 | 26 | Notes 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/PoShKeePass.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/PoShKeePass.psd1 -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/PTLibRestore.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/PTLibRestore.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/SkiaSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/SkiaSharp.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/System.Buffers.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/System.Buffers.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/System.Memory.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/System.Memory.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/System.Numerics.Vectors.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/System.Numerics.Vectors.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/System.Runtime.CompilerServices.Unsafe.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/System.Runtime.CompilerServices.Unsafe.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/System.Security.Cryptography.ProtectedData.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/System.Security.Cryptography.ProtectedData.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/bin/pt.KeePassLibStd.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/PoshKeePass/bin/pt.KeePassLibStd.dll -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v.2.1.3.0 4 | 5 | * Added [#160](https://github.com/PSKeePass/PoShKeePass/issues/160) - Default Database Configuration Profile. 6 | * When set, the `-DatabaseProfileName` parameter is optional, and if not passed it will grab the default profile from the config. 7 | * To Set it up on an existing profile simply use the update command: 8 | 9 | ```powershell 10 | Update-KeePassDatabaseConfigurationProfile -DatabaseProfileName 'name' -Default 11 | ``` 12 | 13 | * To Create a new profile as default use the new command: 14 | 15 | ```powershell 16 | New-KeePassDatabaseConfigurationProfile -DatabaseProfileName 'name' -Default -DatabasePath '' other options 17 | ``` 18 | 19 | * This allows for calls to the main module functions without the `-DatabaseProfileName` parameter such as: 20 | 21 | ```powershell 22 | Get-KeePassEntry -UserName 'aUser' 23 | ``` 24 | 25 | ## v.2.1.2.8 26 | 27 | * Added - [#84](https://github.com/PSKeePass/PoShKeePass/issues/84) - Manage Notes properties on KPGroup Objects. 28 | 29 | * v.2.1.2.6 - Added - [#158](https://github.com/PSKeePass/PoShKeePass/issues/158) - Added Update-KeePassDatabaseConfiguration function and tests. 30 | 31 | * v.2.1.2.5 - Fix - [#157](https://github.com/PSKeePass/PoShKeePass/issues/157) - Set New-KPConnection function back to internal function and no longer exports. 32 | 33 | ## v.2.1.2.4 34 | 35 | * Added Feature [#29](https://github.com/PSKeePass/PoShKeePass/issues/29) - Can now manage the Expiration Time/Enabled State of groups and entry. 36 | 37 | * v.2.1.2.3 - Fix [#64](https://github.com/PSKeePass/PoShKeePass/issues/65) - Review Message for grammar, clarified some messages as well. 38 | 39 | * v.2.1.2.2 - Fix [#156](https://github.com/PSKeePass/PoShKeePass/issues/156) - New-KeePassDatabase will now error out if kdbx file already exists, instead of silently overwriting an existing file. 40 | 41 | * v.2.1.2.1 - Fix [#149](https://github.com/PSKeePass/PoShKeePass/issues/149) - **Breaking Change** New-KeePassGroup and Update-KeePassGroup now return a KeePass PSObject via the ConvertTo-KPPsObject function. 42 | 43 | ## v.2.1.2.0 44 | 45 | * Fix [#148](https://github.com/PSKeePass/PoShKeePass/issues/148) - Can now update an entry multiple times, while retaining history and not through internal lib exception 46 | * Changes to build script 47 | 48 | ## v2.1.1.8 49 | 50 | ### Many fixes, features and improvements, please note the **Breaking Changes** Below 51 | 52 | * Fix [#129](https://github.com/PSKeePass/PoShKeePass/issues/129) - Can now pass Credential Object to `-MasterKey` Parameter 53 | * Fix/Implemented [#69](https://github.com/PSKeePass/PoShKeePass/issues/69) - All primary Functions return a Powershell object rather than a KeePass Object **This Includes Breaking changes!**. 54 | * **Breaking:** 55 | * Since a powershell object is now returned, in order to access the keepass object a child property has been added to the ps object, `.KPEntry` and `.KPGroup`. 56 | * Deprecated the `-AsPlainText` parameter on the `Get-KeePassGroup` function, the call will still work but it will present a warning message. This is being removed as it is no longer necessary. 57 | * **Non-Breaking:** 58 | * Moved how database profile name was being added to the ps object for better performance on conversion. 59 | * Implemented [#93](https://github.com/PSKeePass/PoShKeePass/issues/93) - `Get-KeePassEntry` Now supports `-Title` and `-UserName` parameters also via pipeline. 60 | * Normalized Error handling to remove repetitive code 61 | * Converted extraneous logic to parameter splatting 62 | * Code formatting and removed explict parameter attributes where not necessary. 63 | * Updated Object creation to use the `hashtable` method for performance over the `New-Object` + `Add-Memeber`. 64 | * Fix [#44](https://github.com/PSKeePass/PoShKeePass/issues/44) - Pipeline now Works for `Remove-KeePassDatabaseConfiguration`. 65 | * Implemented [#141](https://github.com/PSKeePass/PoShKeePass/issues/141) - Much stronger Pipeline support. 66 | * `-DatabaseProfileName` no longer needs to be specified to a KPPSObject pipeline recieving function. 67 | * Example: `Get-KeePassEntry -Title 'test' -DatabaseProfileName 'profile' | Remove-KeePassEntry` 68 | * All parent and object paths now are recieved by the pipeline which of course can be overridden by specifing the parameter. 69 | * Fixed [#140](https://github.com/PSKeePass/PoShKeePass/issues/140) and [#138](https://github.com/PSKeePass/PoShKeePass/issues/138) - by removing the `EncodeKeePassLib.ps1` script file as it is no longer in use. 70 | * Fixed [#144](https://github.com/PSKeePass/PoShKeePass/issues/144) - Removed Faultly logic which allowed for the KeePass Icon to get set to blank while updating an object. 71 | * Implemented [#143](https://github.com/PSKeePass/PoShKeePass/issues/143) There are no more dynamic parameters! So all of the gitches have left with them. They still support tab completion by using `Register-ArgumentCompleter`. 72 | * **Breaking Change** as this is only supported in powershell v5 and up, auto complete will not work in older versions. 73 | * Implemented [#118](https://github.com/PSKeePass/PoShKeePass/issues/118) - by adding support for keepasslib version `2.39.1` 74 | * The new file format version between the previous version of `2.34` and the latest apears to be much slower on some operations. 75 | * Testing the new Lib version against the previously suported version `2.34` all worked and appears to be backwards compatible. Also it does not upgrade the file format version. 76 | * Version can easily flipped back by modifying the global variable in the `.psm1` file. 77 | * This fixes [#131](https://github.com/PSKeePass/PoShKeePass/issues/131). 78 | * Fix [#145](https://github.com/PSKeePass/PoShKeePass/issues/145) - Updating a KeePass Entry now updates the modification time in UTC. 79 | * **Breaking Change** - Renamed the `LastAccessTime` and `LastModificationTime` properties to `LastAccessTimeUtc` and `LastModificationTimeUtc` to reflect that they are in UTC. 80 | * Addressed [#88](https://github.com/PSKeePass/PoShKeePass/issues/88) - `Get-KeePassEntry` 81 | * Since a Ps object is now always returned, all fields but the password are in plaintext. Now specifying the `-AsPlainText` will decode the password to plaintext. 82 | * This gives the user better control over when they expose the password as plaintext if need be. 83 | * Another improvement is there is now a `-WithCredential` parameter which adds a `.Credential` property to the return Entry PS Object. 84 | * This is not done by default as it has overhead. 85 | * This gives the user better options and does not require manual creation of the credential. 86 | * **Breaking Change** Since this has been implemeneted the `-AsPsCredential` parameter has been removed. The new method is better as it allows for multiple entries to be returned with thier cred objects instead of limiting it to 1 entry. 87 | * **Breaking Change** - `ConvertTo-KPPSObject` and all returned objects the `.FullPath` property now returns the true full path of the object. The `ParentGroup` property still exists and can be used as an alteranative data source for any lost functionality. 88 | 89 | ## v2.0.5.6 90 | 91 | * Update-KeePassEntry no longer creates a new entry, Entry history is retained, UUID is never changed, All time modificiation fields are now updated when appropriate. 92 | * [#127](https://github.com/PSKeePass/PoShKeePass/issues/127) 93 | * [#123](https://github.com/PSKeePass/PoShKeePass/issues/123) 94 | * [#120](https://github.com/PSKeePass/PoShKeePass/issues/120) 95 | * Code clean up in the internal functions 96 | * Removed unecessary comments. 97 | * Simplified parameter attributes, and formatting. 98 | * Updated error handling to use write-error and simplified handling. 99 | * Normalized repetative checks to their own `Test-X` functions and moved error\null handling inside. 100 | * Test-KPConnection - Checks to see if connection exists and and is open. 101 | * Test-KPPasswordValue - Correctly checks for supported types and moved error handling inside. 102 | * Fixed Dev Tool AutoVersioning Script, now updates psd1 version again. 103 | * Simplified `Import-KPLibrary` function. 104 | * Updated `ConvertTo-KPPSObject` to be construct PSObject differently and gained 86% speed performance improvement. 105 | * Created a `build.ps1` script to build the module for use and publishing to gallery 106 | * Updated `New-KPConnection` to prompt for user MasterKey (keepass passsword) via console prompt `Read-host` instead of `$Host.ui.PromptForCredential()`, this is much faster than loading the gui. 107 | 108 | ## v2.0.4.5 109 | 110 | * #135 - Restructured Module to a more modular structure. Single file per function, seperate root folders for exported functions vs internal functions, (functions, internal). 111 | * Added global variable `$Global:KeePassConfigurationFile` with the path of the config file and updated all references to file. 112 | * Updated formatting of readme and changelog to abide by md standards. 113 | 114 | ## v2.0.4.4 115 | 116 | * Some Community PR here, great help thank you 117 | * #53, #117 `-AsPSCredential` Support to `Get-KeePassEntry` 118 | * `-Title` Parameter Added to `Get-KeePassEntry` 119 | * General bug fixes #115, #116, #120, #123, #127 120 | * `New-KeePassDatabase` function added 121 | 122 | ## v2.0.4.3 123 | 124 | * [#133](https://github.com/PSKeePass/PoShKeePass/issues/133) Fixed 125 | 126 | ## v2.0.4.1 127 | 128 | * [#132](https://github.com/PSKeePass/PoShKeePass/issues/132) Fixed - Windows Defender identifies PoshKeepass as trojan. Please see the issue for more details. 129 | 130 | ## v2.0.4.0 131 | 132 | * #108 Fixed bug by capturing PSCredential to MasterKey variable. 133 | 134 | ## v2.0.3.9 135 | 136 | 1. #92 Added PowerShell Format XML File for creating PowerShell Object views. 137 | 2. #90 Updated default properties returned for KeePass Entries to Include the Notes Property. Did this via the new format XML file. 138 | 3. #67 Consolidated KeePass database connection and authentication functions. Thanks @Ninjigen for your help on this. 139 | 4. #67 Now supports authentication using all three methods as a combination: MasterKey, KeyFile, WindowsAccount. 140 | 5. #100 Fixed bug when using the `-MasterKey` options on and of the functions. The proper variable is now removed. 141 | 6. #95 Added internal function `Restore-KPConfigurationFile`. This is implemented to restore your configuration file from a previous version of the module, when updating from PSGallery. 142 | 7. Moved exported functions to the Module Manifest. 143 | 144 | ## v2.0.3.1 145 | 146 | ### Issue #79 - Added support Icon Management 147 | 148 | * Updated `New` and `Update` Entry and Group functions to support setting and updating Icon values. 149 | * Update `ConvertTo-KPPSObject` to output the IconId. 150 | * Added Pester Tests for adding and updating Icons. 151 | * Update `Get-KPDynamicParameters` to support creating the Icon dynamic param from the `KeePassLib.PwIcon` Enum. 152 | 153 | ## v2.0.3.0 154 | 155 | * Issue 68 - Update getting XML Configuration Document from `Get-Content` to creating a new `System.Xml.XmlDocument` object and using the `Load()` Method. 156 | 157 | ## v2.0.2.9 158 | 159 | * Issue 66 - Updated `ReadString()` method calls to `ReadSafe()` method calls. 160 | * Issue 71 - Updated to Use Proper String Interpolation. 161 | * Issue 72 - Removed Commented out code. 162 | * Issue 73 - Converted to Single Quotes Where Possible. 163 | * Issue 74 - Formatted Code consistently. 164 | 165 | ## v2.0.2.6 166 | 167 | * Updated `New-KeePassPassword` to output as `KeePassLib.Security.ProtectedString` - _This removed the plain text conversion to secure string. 168 | * Updated All New/Update Entry Functions to support _SecureString_ or _ProtectedString_ for the KeePassPassword Parameter. 169 | 170 | ## v2.0.2.1 171 | 172 | ## Added Dynamic Parameter MasterKey 173 | 174 | Added optional Parameter `-MasterKey` to core functions. 175 | 176 | This parameter was added to allow for easier scripting with databases that use a masterkey password. 177 | 178 | Previously if a masterkey password was used the updated functions would prompt the user of the masterkey after the funciton was called. This is still the behaviour if the masterkey is required but not specified. 179 | 180 | Functions Updated: 181 | 182 | 1. `New-KeePassEntry` 183 | 2. `New-KeePassGroup` 184 | 3. `Get-KeePassEntry` 185 | 4. `Get-KeePassGroup` 186 | 5. `Update-KeePassEntry` 187 | 6. `Update-KeePassGroup` 188 | 7. `Remove-KeePassEntry` 189 | 8. `Remove-KeePassGroup` 190 | 191 | ## Simplfied Dynamic Parameters 192 | 193 | Internally created a function that builds the commonly used Dynamic Parameters. 194 | 195 | `Get-KPDynamicParameter` 196 | 197 | ## Simplfied Building an Automatic Database Connection 198 | 199 | Internally created a function that builds the Database connection based off of the Database Configuration Profile Specified. 200 | 201 | `Invoke-KPConnection` 202 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Christian Lehrer, John Klann 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/PoshKeePass/readme.md: -------------------------------------------------------------------------------- 1 | # PowerShell KeePass 2 | 3 | [PoShKeePass](https://www.powershellgallery.com/packages/PoShKeePass) is a PowerShell module that combines the ease of the PowerShell cli and the extensibility of the [KeePassLib API](http://keepass.info/help/v2/setup.html) to provide a powerful and easy to use management and automating platform for [KeePass](http://keepass.info/) databases. 4 | 5 | ## Features 6 | 7 | 1. **Database Configuration Profiles** - Supports mutliple databases and authentication options. 8 | 2. **Getting, Creating, Updating, and Removing KeePass Entries and Groups** - All of these perform as much automatic database authentication as possible using the database configuration profile. For databases that use a masterkey (password) it will prompt for it. 9 | 3. **Generating KeePass Passwords** - Supports most character sets and advanced keepass options. Also supports creating password profiles that can be specified to create a new password with the same rule set. 10 | 11 | ## Getting Started 12 | 13 | ### Install 14 | 15 | ```powershell 16 | Install-Module -Name PoShKeePass 17 | ``` 18 | 19 | ### Documentation 20 | 21 | Please check out our [Getting Started](https://github.com/PSKeePass/PoShKeePass/wiki/Getting-Started) documentation on our [wiki](https://github.com/PSKeePass/PoShKeePass/wiki). 22 | 23 | ## Important Notes & Reminders 24 | 25 | 1. Please always keep up to date **backups** of your KeePass database files .kdbx and .key files. 26 | 2. The module uses the KeePassLib 2.3.x which is embedded in the module file. 27 | 3. This module was built and tested in PowerShell 5 on Windows 10 but should work in PowerShell 4 and Windows 8.1 and Server 2012 R2 and up. It may work in some earlier versions but is currently untested and not supported. If you come across issues create an issue and I will look into fixing it or create a pull request. 28 | 4. There is an underlying framework that I wrote into the module to make all of the api calls that I will eventually expose for advanced scripting. 29 | 30 | ## Changelog 31 | 32 | Please review the [changelog document](https://github.com/PSKeePass/PoShKeePass/blob/master/changelog.md) for a full history. 33 | 34 | ## v.2.1.3.0 35 | 36 | * Added [#160](https://github.com/PSKeePass/PoShKeePass/issues/160) - Default Database Configuration Profile. 37 | * When set, the `-DatabaseProfileName` parameter is optional, and if not passed it will grab the default profile from the config. 38 | * To Set it up on an existing profile simply use the update command: 39 | 40 | ```powershell 41 | Update-KeePassDatabaseConfigurationProfile -DatabaseProfileName 'name' -Default 42 | ``` 43 | 44 | * To Create a new profile as default use the new command: 45 | 46 | ```powershell 47 | New-KeePassDatabaseConfigurationProfile -DatabaseProfileName 'name' -Default -DatabasePath '' other options 48 | ``` 49 | 50 | * This allows for calls to the main module functions without the `-DatabaseProfileName` parameter such as: 51 | 52 | ```powershell 53 | Get-KeePassEntry -UserName 'aUser' 54 | ``` 55 | 56 | ## v.2.1.2.8 57 | 58 | * Added - [#84](https://github.com/PSKeePass/PoShKeePass/issues/84) - Manage Notes properties on KPGroup Objects. 59 | 60 | * v.2.1.2.6 - Added - [#158](https://github.com/PSKeePass/PoShKeePass/issues/158) - Added Update-KeePassDatabaseConfiguration function and tests. 61 | 62 | * v.2.1.2.5 - Fix - [#157](https://github.com/PSKeePass/PoShKeePass/issues/157) - Set New-KPConnection function back to internal function and no longer exports. 63 | 64 | ## v.2.1.2.4 65 | 66 | * Added Feature [#29](https://github.com/PSKeePass/PoShKeePass/issues/29) - Can now manage the Expiration Time/Enabled State of groups and entry. 67 | 68 | * v.2.1.2.3 - Fix [#64](https://github.com/PSKeePass/PoShKeePass/issues/65) - Review Message for grammar, clarified some messages as well. 69 | 70 | * v.2.1.2.2 - Fix [#156](https://github.com/PSKeePass/PoShKeePass/issues/156) - New-KeePassDatabase will now error out if kdbx file already exists, instead of silently overwriting an existing file. 71 | 72 | * v.2.1.2.1 - Fix [#149](https://github.com/PSKeePass/PoShKeePass/issues/149) - **Breaking Change** New-KeePassGroup and Update-KeePassGroup now return a KeePass PSObject via the ConvertTo-KPPsObject function. 73 | 74 | ## v.2.1.2.0 75 | 76 | * Fix [#148](https://github.com/PSKeePass/PoShKeePass/issues/148) - Can now update an entry multiple times, while retaining history and not through internal lib exception 77 | * Changes to build script 78 | 79 | ## v2.1.1.8 80 | 81 | ### Many fixes, features and improvements, please note the **Breaking Changes** Below 82 | 83 | * Fix [#129](https://github.com/PSKeePass/PoShKeePass/issues/129) - Can now pass Credential Object to `-MasterKey` Parameter 84 | * Fix/Implemented [#69](https://github.com/PSKeePass/PoShKeePass/issues/69) - All primary Functions return a Powershell object rather than a KeePass Object **This Includes Breaking changes!**. 85 | * **Breaking:** 86 | * Since a powershell object is now returned, in order to access the keepass object a child property has been added to the ps object, `.KPEntry` and `.KPGroup`. 87 | * Deprecated the `-AsPlainText` parameter on the `Get-KeePassGroup` function, the call will still work but it will present a warning message. This is being removed as it is no longer necessary. 88 | * **Non-Breaking:** 89 | * Moved how database profile name was being added to the ps object for better performance on conversion. 90 | * Implemented [#93](https://github.com/PSKeePass/PoShKeePass/issues/93) - `Get-KeePassEntry` Now supports `-Title` and `-UserName` parameters also via pipeline. 91 | * Normalized Error handling to remove repetitive code 92 | * Converted extraneous logic to parameter splatting 93 | * Code formatting and removed explict parameter attributes where not necessary. 94 | * Updated Object creation to use the `hashtable` method for performance over the `New-Object` + `Add-Memeber`. 95 | * Fix [#44](https://github.com/PSKeePass/PoShKeePass/issues/44) - Pipeline now Works for `Remove-KeePassDatabaseConfiguration`. 96 | * Implemented [#141](https://github.com/PSKeePass/PoShKeePass/issues/141) - Much stronger Pipeline support. 97 | * `-DatabaseProfileName` no longer needs to be specified to a KPPSObject pipeline recieving function. 98 | * Example: `Get-KeePassEntry -Title 'test' -DatabaseProfileName 'profile' | Remove-KeePassEntry` 99 | * All parent and object paths now are recieved by the pipeline which of course can be overridden by specifing the parameter. 100 | * Fixed [#140](https://github.com/PSKeePass/PoShKeePass/issues/140) and [#138](https://github.com/PSKeePass/PoShKeePass/issues/138) - by removing the `EncodeKeePassLib.ps1` script file as it is no longer in use. 101 | * Fixed [#144](https://github.com/PSKeePass/PoShKeePass/issues/144) - Removed Faultly logic which allowed for the KeePass Icon to get set to blank while updating an object. 102 | * Implemented [#143](https://github.com/PSKeePass/PoShKeePass/issues/143) There are no more dynamic parameters! So all of the gitches have left with them. They still support tab completion by using `Register-ArgumentCompleter`. 103 | * **Breaking Change** as this is only supported in powershell v5 and up, auto complete will not work in older versions. 104 | * Implemented [#118](https://github.com/PSKeePass/PoShKeePass/issues/118) - by adding support for keepasslib version `2.39.1` 105 | * The new file format version between the previous version of `2.34` and the latest apears to be much slower on some operations. 106 | * Testing the new Lib version against the previously suported version `2.34` all worked and appears to be backwards compatible. Also it does not upgrade the file format version. 107 | * Version can easily flipped back by modifying the global variable in the `.psm1` file. 108 | * This fixes [#131](https://github.com/PSKeePass/PoShKeePass/issues/131). 109 | * Fix [#145](https://github.com/PSKeePass/PoShKeePass/issues/145) - Updating a KeePass Entry now updates the modification time in UTC. 110 | * **Breaking Change** - Renamed the `LastAccessTime` and `LastModificationTime` properties to `LastAccessTimeUtc` and `LastModificationTimeUtc` to reflect that they are in UTC. 111 | * Addressed [#88](https://github.com/PSKeePass/PoShKeePass/issues/88) - `Get-KeePassEntry` 112 | * Since a Ps object is now always returned, all fields but the password are in plaintext. Now specifying the `-AsPlainText` will decode the password to plaintext. 113 | * This gives the user better control over when they expose the password as plaintext if need be. 114 | * Another improvement is there is now a `-WithCredential` parameter which adds a `.Credential` property to the return Entry PS Object. 115 | * This is not done by default as it has overhead. 116 | * This gives the user better options and does not require manual creation of the credential. 117 | * **Breaking Change** Since this has been implemeneted the `-AsPsCredential` parameter has been removed. The new method is better as it allows for multiple entries to be returned with thier cred objects instead of limiting it to 1 entry. 118 | * **Breaking Change** - `ConvertTo-KPPSObject` and all returned objects the `.FullPath` property now returns the true full path of the object. The `ParentGroup` property still exists and can be used as an alteranative data source for any lost functionality. 119 | 120 | ## Known Issues 121 | 122 | See the [Known-Issue](https://github.com/PSKeePass/PoShKeePass/issues?q=is%3Aissue+is%3Aopen+label%3AKnown-Issue) tag to get a list of known issues and their status. 123 | 124 | ## Contributing 125 | 126 | * If you are insterested in fixing issues and contributing directly to the code base, please see the documentation on [How to Contribute](https://github.com/PSKeePass/PoShKeePass/blob/master/contribute.md). 127 | * If you come across a bug or have a feature request feel free to create an issue with the appropriate label. 128 | 129 | ## Shout-Outs 130 | 131 | * PSKeePass would like to thank [Jason Fossen](https://github.com/JasonFossen) for his [initial work](https://cyber-defense.sans.org/blog/2015/08/13/powershell-for-keepass-sample-script) with KeePass in PowerShell. 132 | * PSKeePass would like to thank [Christian Lehrer](https://github.com/chritea) for his powershell keepass work and contributions. 133 | * PSKeePass would like to thank [Ninjigen](https://github.com/Ninjigen) for his powershell keepass work and contributions. 134 | * PSKeePass would like to thank [Andrzej Pilacik](http://www.apdba.com/) (aka @cypisek77) for his review and feedback on documentation and over all rubber ducking. 135 | 136 | ## License 137 | 138 | Copyright (c) 2019 [John Klann](https://github.com/jkdba). All rights reserved. 139 | 140 | Licensed under the [MIT](https://github.com/PSKeePass/PoShKeePass/blob/master/license) License. 141 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/Public/Register-KeePassSecretVault.ps1: -------------------------------------------------------------------------------- 1 | function Register-KeepassSecretVault { 2 | <# 3 | .SYNOPSIS 4 | Registers a Keepass Vault with the Secret Management engine 5 | .DESCRIPTION 6 | Enables you to register a keepass vault with the secret management engine, with more discoverable parameters and 7 | safety checks 8 | .EXAMPLE 9 | PS C:\> Register-KeepassSecretVault -Path $HOME/Desktop/MyVault.kdbx 10 | Explanation of what the example does 11 | #> 12 | 13 | [CmdletBinding(DefaultParameterSetName = 'UseMasterPassword')] 14 | param( 15 | #Path to your kdbx database file 16 | [Parameter(Mandatory,ValueFromPipeline,ValueFromPipelineByPropertyName)][String]$Path, 17 | #Name of your secret management vault. Defaults to the base filename 18 | [String]$Name, 19 | #Path to your kdbx keyfile path if you use one. Only v1 keyfiles (2.44 and older) are currently supported 20 | [String]$KeyPath, 21 | #Prompt for a master password for the vault 22 | [Switch]$UseMasterPassword, 23 | #Use your Windows Login account as an authentication factor for the vault 24 | [Switch]$UseWindowsAccount, 25 | #Automatically create a keepass database with the specifications you provided 26 | [Parameter(ParameterSetName='Create')][Switch]$Create, 27 | #Specify the master password to use when automatically creating a vault 28 | [Parameter(ParameterSetName='Create')][SecureString]$MasterPassword, 29 | #Report key titles as full paths including folders. Useful if you want to view conflicting Keys 30 | [Switch]$ShowFullTitle, 31 | #Show Recycle Bin entries 32 | [Switch]$ShowRecycleBin, 33 | #Don't validate the vault operation upon registration. This is useful for pre-staging 34 | #vaults or vault configurations in deployments. 35 | [Parameter(ParameterSetName='SkipValidate')][Switch]$SkipValidate 36 | ) 37 | 38 | $ErrorActionPreference = 'Stop' 39 | if (-not ($SkipValidate -or $Create)) { 40 | $Path = Resolve-Path $Path 41 | } 42 | if (-not $Name) { $Name = ([IO.FileInfo]$Path).BaseName } 43 | if ($UseWindowsAccount -and -not ($PSEdition -eq 'Desktop' -or $IsWindows)) { 44 | throw [NotSupportedException]'-UseWindowsAccount parameter is only supported on Windows' 45 | } 46 | if (-not $UseMasterPassword -and -not $UseWindowsAccount -and -not $KeyPath) { 47 | throw [InvalidOperationException]'No authentication methods specified. You must specify at least one of: UseMasterPassword, UseWindowsAccount, or KeyPath' 48 | } 49 | if ($Create) { 50 | $ConnectKPDBParams = @{ 51 | Path = $Path 52 | KeyPath = $KeyPath 53 | UseWindowsAccount = $UseWindowsAccount 54 | Create = $Create 55 | MasterPassword = $MasterPassword 56 | } 57 | $dbConnection = Connect-KeePassDatabase @ConnectKPDBParams 58 | if (-not $dbConnection) {throw 'Connect-KeePassDatabase was executed but a database connection was not returned. This should not happen.'} 59 | } 60 | 61 | #BUG: Workaround for https://github.com/PowerShell/SecretManagement/issues/103 62 | if (Get-Module SecretManagement.KeePass -ErrorAction SilentlyContinue -OutVariable KeePassModule) { 63 | $ModuleName = $KeePassModule.Path 64 | } else { 65 | $ModuleName = 'SecretManagement.KeePass' 66 | } 67 | 68 | Register-SecretVault -ModuleName $ModuleName -Name $Name -VaultParameters @{ 69 | Path = $Path 70 | UseMasterPassword = $UseMasterPassword.IsPresent 71 | UseWindowsAccount = $UseWindowsAccount.IsPresent 72 | KeyPath = $KeyPath 73 | ShowFullTitle = $ShowFullTitle.IsPresent 74 | ShowRecycleBin = $ShowRecycleBin.IsPresent 75 | } 76 | 77 | if (-not (Get-SecretVault -Name $Name)) { throw 'Register-SecretVault did not return an error but the vault is not registered.' } 78 | #Create does the same validation 79 | if (-not $SkipValidate -and -not $Create) { 80 | if (-not (Test-SecretVault -VaultName $Name)) { 81 | Unregister-SecretVault -Name $Name -ErrorAction SilentlyContinue 82 | throw "$Name is an invalid vault configuration, removing. Consider using -SkipValidate if you wish to pre-load a configuration without testing it" 83 | } 84 | } 85 | 86 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/Public/Unlock-KeePassSecretVault.ps1: -------------------------------------------------------------------------------- 1 | function Unlock-KeePassSecretVault { 2 | <# 3 | .SYNOPSIS 4 | Enables the entry of a master password prior to vault activities for unattended scenarios. 5 | If registering a vault for the first time unattended, be sure to use the -SkipValidate parameter of Register-KeepassSecretVault 6 | .EXAMPLE 7 | Get-SecretVault 'MyKeepassVault' | Unlock-KeePassSecretVault -Password $MySecureString 8 | .EXAMPLE 9 | Unlock-KeePassSecretVault -Name 'MyKeepassVault' -Password $MySecureString 10 | #> 11 | param ( 12 | [Parameter(Mandatory)][SecureString]$Password, 13 | [Parameter(Mandatory,ValueFromPipelineByPropertyName)][String]$Name 14 | ) 15 | 16 | Write-PSFMessage -Level Warning 'DEPRECATED: This command has been deprecated. Please use the SecretManagement command Unlock-SecretVault instead.' 17 | Microsoft.PowerShell.SecretManagement\Unlock-SecretVault -Password $Password -Name $Name 18 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Private/ConvertTo-ReadOnlyDictionary.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Collections.ObjectModel 2 | using namespace System.Collections.Generic 3 | function ConvertTo-ReadOnlyDictionary { 4 | <# 5 | .SYNOPSIS 6 | Converts a hashtable to a ReadOnlyDictionary[String,Object]. Needed for SecretInformation 7 | #> 8 | [CmdletBinding()] 9 | param( 10 | [Parameter(ValueFromPipeline)][hashtable]$hashtable 11 | ) 12 | process { 13 | $dictionary = [SortedDictionary[string,object]]::new([StringComparer]::OrdinalIgnoreCase) 14 | $hashtable.GetEnumerator().foreach{ 15 | $dictionary[$_.Name] = $_.Value 16 | } 17 | [ReadOnlyDictionary[string,object]]::new($dictionary) 18 | } 19 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Private/GetKeepassParams.ps1: -------------------------------------------------------------------------------- 1 | function GetKeepassParams ([String]$VaultName, [Hashtable]$AdditionalParameters) { 2 | $KeepassParams = @{} 3 | if ($VaultName) { 4 | $KeepassParams.KeePassConnection = (Get-Variable -Scope Script -Name "Vault_$VaultName").Value 5 | } 6 | return $KeepassParams 7 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Private/Test-DBChanged.ps1: -------------------------------------------------------------------------------- 1 | function Test-DBChanged ($dbConnection) { 2 | [string]$currentDbFileHash = (Get-FileHash -Path $dbConnection.IOConnectionInfo.Path).Hash 3 | [byte[]]$dbHashBytes = $dbConnection.HashOfFileOnDisk 4 | 5 | #Convert to String 6 | [string]$dbHash = $dbHashBytes.foreach{[String]::Format('{0:X2}', $_)} -join '' 7 | 8 | 9 | #Return true or false 10 | $currentDbFileHash -ne $dbHash 11 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Private/Test-Verbose.ps1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/SecretManagement.KeePass.Extension/Private/Test-Verbose.ps1 -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Private/Unlock-SecureString.ps1: -------------------------------------------------------------------------------- 1 | function Unlock-SecureString ([SecureString]$SecureString) { 2 | <# 3 | .SYNOPSIS 4 | Compatibility function to convert a secure string to plain text 5 | .OUTPUT 6 | String 7 | #> 8 | if ($PSVersionTable.PSVersion -ge '6.0.0') { 9 | ConvertFrom-SecureString -AsPlainText -SecureString $SecureString 10 | } else { 11 | #Legacy Windows Powershell Workaround Method 12 | [PSCredential]::new('SecureString',$SecureString).GetNetworkCredential().Password 13 | } 14 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Private/VaultError.ps1: -------------------------------------------------------------------------------- 1 | function VaultError ([String]$Message) { 2 | <# 3 | .SYNOPSIS 4 | Takes a terminating error and first writes it as a non-terminating error to the user to better surface the issue. 5 | #> 6 | 7 | #FIXME: Use regular errors if https://github.com/PowerShell/SecretManagement/issues/102 is resolved 8 | Write-PSFMessage -Level Error "Vault ${VaultName}: $Message" 9 | throw "Vault ${VaultName}: $Message" 10 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Connect-KeepassDatabase.ps1: -------------------------------------------------------------------------------- 1 | using namespace KeePassLib 2 | using namespace KeePassLib.Keys 3 | using namespace KeePassLib.Serialization 4 | using namespace KeePassLib.Interfaces 5 | using namespace System.Runtime.InteropServices 6 | 7 | function Connect-KeePassDatabase { 8 | <# 9 | .SYNOPSIS 10 | Open a connection to a keepass database 11 | #> 12 | param ( 13 | #Path to the Keepass database 14 | [Parameter(Mandatory)][String]$Path, 15 | #Prompt for a master password 16 | [Switch]$UseMasterPassword, 17 | #The master password to unlock the database 18 | [SecureString]$MasterPassword, 19 | #The path to the key file for the database 20 | [String]$KeyPath, 21 | #Whether to use a secure key stored via DPAPI in your windows profile 22 | [Switch]$UseWindowsAccount, 23 | #Create a new database at the specified path. Will error if a database does not exist at the specified path 24 | [Switch]$Create, 25 | #Allow clobbering an existing database 26 | [Switch]$AllowClobber 27 | ) 28 | 29 | $DBCompositeKey = [CompositeKey]::new() 30 | 31 | if (-not $MasterPassword -and -not $KeyPath -and -not $UseWindowsAccount) { 32 | Write-PSFMessage -Level Verbose "No vault authentication mechanisms specified. Assuming you wanted to prompt for the Master Password" 33 | $UseMasterPassword = $true 34 | } 35 | 36 | if ($UseMasterPassword -and -not $MasterPassword) { 37 | $CredentialParams = @{ 38 | Username = 'Keepass Master Password' 39 | Message = "Enter the Keepass Master password for: $Path" 40 | } 41 | #PS7+ Only 42 | if ($PSEdition -ne 'Desktop') { 43 | $CredentialParams.Title = 'Keepass Master Password' 44 | } 45 | $MasterPassword = (Get-Credential @CredentialParams).Password 46 | } 47 | 48 | #NOTE: Order in which the CompositeKey is created is important and must follow the order of : MasterKey, KeyFile, Windows Account 49 | if ($MasterPassword) { 50 | $DBCompositeKey.AddUserKey( 51 | [KcpPassword]::new( 52 | #Decode SecureString 53 | [Marshal]::PtrToStringUni([Marshal]::SecureStringToBSTR($MasterPassword)) 54 | ) 55 | ) 56 | } 57 | 58 | if ($KeyPath) { 59 | 60 | if (-not (Test-Path $KeyPath)) { 61 | if ($Create) { 62 | #Create a new key 63 | [KcpKeyFile]::Create( 64 | $KeyPath, 65 | $null 66 | ) 67 | } else { 68 | #Will emit a path not found error 69 | Resolve-Path $KeyPath 70 | } 71 | } else { 72 | Write-PSFMessage -Level Verbose "A keepass key file was already found at $KeyPath. Reusing this key for safety. Please manually delete this key if you wish to use a new one" 73 | } 74 | 75 | $resolvedKeyPath = Resolve-Path $KeyPath 76 | # Assume UNC path if no drive present. 77 | if ($Null -eq $resolvedKeyPath.Drive) { 78 | $dbCompositeKey.AddUserKey( 79 | [KcpKeyFile]::new( 80 | $resolvedKeyPath.ProviderPath, #Path to keyfile 81 | $true #Error if it is a database file 82 | ) 83 | ) 84 | } else { 85 | $dbCompositeKey.AddUserKey( 86 | [KcpKeyFile]::new( 87 | $resolvedKeyPath, #Path to keyfile 88 | $true #Error if it is a database file 89 | ) 90 | ) 91 | } 92 | } 93 | 94 | if ($UseWindowsAccount) { 95 | if ($PSVersionTable.PSVersion -gt '5.99.99' -and -not $IsWindows) { 96 | throw [NotSupportedException]'The -UseWindowsAccount parameter is only supported on a Windows Platform' 97 | } 98 | $DBCompositeKey.AddUserKey([KcpUserAccount]::new()) 99 | } 100 | 101 | $ParentPath = (Resolve-Path ($Path | Split-Path)).ProviderPath 102 | $DBFile = $Path | Split-Path -Leaf 103 | $resolvedPath = Join-Path -Path $ParentPath -ChildPath $DBFile 104 | 105 | $DBConnection = [PWDatabase]::new() 106 | $DBConnectionInfo = [IOConnectionInfo]::FromPath($resolvedPath) 107 | 108 | if ($Create) { 109 | if (-not $AllowClobber -and (Test-Path $resolvedPath)) { 110 | throw "-Create was specified but a database already exists at $resolvedPath. Please specify -AllowClobber to overwrite the database." 111 | } 112 | $DBConnection.New( 113 | $DBConnectionInfo, 114 | $DBCompositeKey 115 | ) 116 | $DBConnection.Save($null) 117 | } 118 | 119 | #Establish the connection 120 | 121 | $DBConnection.Open( 122 | $DBConnectionInfo, 123 | $DBCompositeKey, 124 | $null #No status logger 125 | ) 126 | if (-not $DBConnection.IsOpen) { throw "Unable to connect to the database at $resolvedPath. Please check you supplied proper credentials" } 127 | $DBConnection 128 | } 129 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Get-Secret.ps1: -------------------------------------------------------------------------------- 1 | function Get-Secret { 2 | [CmdletBinding()] 3 | param ( 4 | [string]$Name, 5 | [Alias('Vault')][string]$VaultName, 6 | [Alias('VaultParameters')][hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters 7 | ) 8 | if ($AdditionalParameters.Verbose) { $VerbosePreference = 'continue' } 9 | 10 | Write-PSFMessage "Searching Entry with Name=$Name" 11 | if (-not (Test-SecretVault -VaultName $vaultName -AdditionalParameters $AdditionalParameters)) { 12 | Write-PSFMessage -Level Error 'There appears to be an issue with the vault (Test-SecretVault returned false)' -Target "$Name" 13 | throw 'There appears to be an issue with the vault (Test-SecretVault returned false)' 14 | } 15 | 16 | if (-not $Name) { 17 | Write-PSFMessage -Level Error 'You must specify a secret Name' -Target "$Name" 18 | throw 'You must specify a secret Name' 19 | } 20 | 21 | $KeepassParams = GetKeepassParams $VaultName $AdditionalParameters 22 | 23 | if ($Name) { $KeePassParams.Title = $Name } 24 | $keepassGetResult = Get-SecretInfo -Vault $vaultName -Filter $Name -AsKPPSObject 25 | 26 | if ($null -eq $keepassGetResult) { 27 | Write-PSFMessage "No Keepass Entry found" -Target $Name 28 | return 29 | } 30 | if ($keepassGetResult.count -gt 1) { 31 | Write-PSFMessage -Level Error "Multiple ambiguous entries found for $Name, please remove the duplicate entry or specify the full path of the secret" -Target "$Name" 32 | throw "Multiple ambiguous entries found for $Name, please remove the duplicate entry or specify the full path of the secret" 33 | } 34 | $result = if (-not $keepassGetResult.Username) { 35 | $keepassGetResult.Password 36 | } 37 | else { 38 | [PSCredential]::new($KeepassGetResult.UserName, $KeepassGetResult.Password) 39 | } 40 | return $result 41 | } 42 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Get-SecretInfo.ps1: -------------------------------------------------------------------------------- 1 | using namespace Microsoft.PowerShell.SecretManagement 2 | using namespace System.Collections.ObjectModel 3 | function Get-SecretInfo { 4 | [CmdletBinding()] 5 | param( 6 | [Alias('Name')][string]$Filter, 7 | [Alias('Vault')][string]$VaultName = (Get-SecretVault).VaultName, 8 | [Alias('VaultParameters')][hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters, 9 | [Switch]$AsKPPSObject 10 | ) 11 | if ($AdditionalParameters.Verbose) {$VerbosePreference = 'continue'} 12 | 13 | if (-not (Test-SecretVault -VaultName $vaultName)) { 14 | Write-PSFMessage -Level Error 'There appears to be an issue with the vault (Test-SecretVault returned false)' 15 | return $false 16 | } 17 | 18 | $KeepassParams = GetKeepassParams -VaultName $VaultName -AdditionalParameters $AdditionalParameters 19 | $KeepassGetResult = Get-KPEntry @KeepassParams | ConvertTo-KPPSObject 20 | if (-not $AdditionalParameters.ShowRecycleBin) { 21 | $KeepassGetResult = $KeepassGetResult | Where-Object FullPath -notmatch '^[^\/]+?\/Recycle ?Bin$' 22 | } 23 | 24 | #TODO: Split this off into private function for testing 25 | function Get-KPSecretName ([PSCustomObject]$KPPSObject) { 26 | <# 27 | .SYNOPSIS 28 | Gets the secret name for the vault context, contingent on some parameters 29 | WARNING: Relies on external context $AdditionalParameters 30 | #> 31 | if ($AdditionalParameters.ShowFullTitle) { 32 | #Strip everything before the first / 33 | $i = $KPPSObject.FullPath.IndexOf('/') 34 | $prefix = if ($i -eq -1) {$null} else { 35 | $KPPSObject.FullPath.Substring($i+1) 36 | } 37 | #Output Prefix/Title 38 | if ($prefix) { 39 | return $prefix,$KPPSObject.Title -join '/' 40 | } else { 41 | return $KPPSObject.Title 42 | } 43 | } else { 44 | return $KPPSObject.Title 45 | } 46 | } 47 | 48 | if ($Filter) { 49 | $KeepassGetResult = $KeepassGetResult | Where-Object { 50 | (Get-KPSecretName $PSItem) -like $Filter 51 | } 52 | } 53 | 54 | #Used by internal commands like Get-Secret 55 | if ($AsKPPSObject) { 56 | return $KeepassGetResult 57 | } 58 | 59 | [Object[]]$secretInfoResult = $KeepassGetResult | Foreach-Object { 60 | if (-not $PSItem.Title) { 61 | Write-PSFMessage -Level Warning "Keepass Entry with blank title found at $($PSItem.FullPath). These are not currently supported and will be omitted" 62 | return 63 | } 64 | 65 | [ReadOnlyDictionary[String,Object]]$metadata = [ordered]@{ 66 | UUID = $PSItem.uuid.ToHexString() 67 | Title = $PSItem.Title 68 | ParentGroup = $PSItem.ParentGroup 69 | Path = $PSItem.FullPath,$PSItem.Title -join '/' 70 | Notes = $PSItem.Notes 71 | URL = $PSItem.Url 72 | Tags = $PSItem.Tags -join ', ' 73 | Created = $PSItem.CreationTime 74 | Accessed = $PSItem.LastAccessTimeUtc 75 | Modified = $PSItem.LastModifiedTimeUtc 76 | Moved = $PSItem.LocationChanged 77 | IconName = $PSItem.IconId 78 | UsageCount = $PSItem.UsageCount 79 | Expires = if ($Expires) {$PSItem.ExpireTime} 80 | } | ConvertTo-ReadOnlyDictionary 81 | 82 | #TODO: Find out why the fully qualified is required on Linux even though using Namespace is defined above 83 | [Microsoft.PowerShell.SecretManagement.SecretInformation]::new( 84 | (Get-KPSecretName $PSItem), #string name 85 | #TODO: Add logic to mark as securestring if there is no username 86 | [Microsoft.PowerShell.SecretManagement.SecretType]::PSCredential, #SecretType type 87 | $VaultName, #string vaultName 88 | $metadata #ReadOnlyDictionary[string,object] metadata 89 | ) 90 | } 91 | 92 | [Object[]]$sortedInfoResult = $secretInfoResult | Sort-Object -Unique -Property Name 93 | if ($sortedInfoResult.count -lt $secretInfoResult.count) { 94 | $nonUniqueFilteredRecords = Compare-Object $sortedInfoResult $secretInfoResult -Property Name | Where-Object SideIndicator -eq '=>' 95 | Write-PSFMessage -Level Error "Vault ${VaultName}: Entries with non-unique titles were detected, the duplicates were filtered out. $(if (-not $additionalParameters.ShowFullTitle) {'Consider adding the ShowFullTitle VaultParameter to your vault registration'})" 96 | Write-PSFMessage -Level Error "Vault ${VaultName}: Filtered Non-Unique Titles: $($nonUniqueFilteredRecords.Name -join ', ')" 97 | } 98 | $sortedInfoResult 99 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Remove-Secret.ps1: -------------------------------------------------------------------------------- 1 | function Remove-Secret { 2 | [CmdletBinding()] 3 | param ( 4 | [ValidateNotNullOrEmpty()][string]$Name, 5 | [Alias('Vault')][string]$VaultName, 6 | [Alias('VaultParameters')][hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters 7 | ) 8 | if ($AdditionalParameters.Verbose) {$VerbosePreference = 'continue'} 9 | if (-not (Test-SecretVault -VaultName $vaultName)) { 10 | VaultError 'There appears to be an issue with the vault (Test-SecretVault returned false)' 11 | return $false 12 | } 13 | $KeepassParams = GetKeepassParams $VaultName $AdditionalParameters 14 | 15 | $GetKeePassResult = Get-SecretInfo -VaultName $VaultName -Name $Name -AsKPPSObject 16 | if ($GetKeePassResult.count -gt 1) { 17 | VaultError "There are multiple entries with the name $Name and Remove-Secret will not proceed for safety." 18 | return $false 19 | } 20 | if (-not $GetKeePassResult) { 21 | VaultError "No Keepass Entry named $Name found" 22 | return $false 23 | } 24 | 25 | Remove-KPEntry @KeepassParams -KeePassEntry $GetKeePassResult.KPEntry -ErrorAction stop -Confirm:$false 26 | 27 | return $true 28 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Set-Secret.ps1: -------------------------------------------------------------------------------- 1 | using namespace KeepassLib.Security 2 | function Set-Secret { 3 | [CmdletBinding()] 4 | param ( 5 | [string]$Name, 6 | [object]$Secret, 7 | [Alias('Vault')][string]$VaultName, 8 | [Alias('VaultParameters')][hashtable]$AdditionalParameters = (Get-SecretVault -Name $VaultName).VaultParameters 9 | ) 10 | if ($AdditionalParameters.Verbose) { $VerbosePreference = 'continue' } 11 | 12 | if (-not $Name) { 13 | Write-PSFMessage -Level Error ([NotSupportedException]'The -Name parameter is mandatory for the KeePass vault') 14 | return $false 15 | } 16 | if (-not (Test-SecretVault -VaultName $vaultName)) { 17 | Write-PSFMessage -Level Error 'There appears to be an issue with the vault (Test-SecretVault returned false)' 18 | return $false 19 | } 20 | $KeepassParams = GetKeepassParams $VaultName $AdditionalParameters 21 | # Write-PSFMessage -Level Verbose "KeepassParams=$($KeepassParams|ConvertTo-JSon -Compress)" -Tag Sherlock -Target $Name 22 | # Write-PSFMessage -Level Verbose "AdditionalParameters=$($AdditionalParameters|ConvertTo-JSon -Compress)" -Tag Sherlock -Target $Name 23 | 24 | 25 | switch ($Secret.GetType()) { 26 | ([String]) { 27 | $KeepassParams.Username = $null 28 | $KeepassParams.KeepassPassword = [ProtectedString]::New($true, $Secret) 29 | break 30 | } 31 | ([SecureString]) { 32 | $KeepassParams.Username = $null 33 | $KeepassParams.KeepassPassword = [ProtectedString]::New($true, (Unlock-SecureString $Secret)) 34 | break 35 | } 36 | ([PSCredential]) { 37 | $KeepassParams.Username = $Secret.Username 38 | $KeepassParams.KeepassPassword = [ProtectedString]::New($true, $Secret.GetNetworkCredential().Password) 39 | break 40 | } 41 | default { 42 | Write-PSFMessage -Level Error ([NotImplementedException]'This vault provider only accepts string, securestring, and PSCredential secrets') 43 | return $false 44 | } 45 | } 46 | 47 | if (Get-SecretInfo -Name $Name -Vault $VaultName) { 48 | Write-PSFMessage "Updating Keepass Entry" -Target $Name -Tag Update 49 | 50 | try { 51 | # $KeepassEntry = Get-SecretInfo -Name $Name -Vault $VaultName -AsKPPSObject 52 | # Need to get the original KPEntry Object for modification 53 | $KeepassParamsGetKPEntry = GetKeepassParams $VaultName $AdditionalParameters 54 | 55 | $KeepassResults = Get-KPEntry @KeepassParamsGetKPEntry -Title $Name 56 | if (-not $AdditionalParameters.ShowRecycleBin) { 57 | $countBeforeRemoval = $KeepassResults.count 58 | Write-PSFMessage -Level Verbose "Removing found entries with FullPath containing 'RecycleBin'" 59 | # $KeepassResults = $KeepassResults | Where-Object FullPath -notmatch '^[^\/]+?\/Recycle ?Bin$' 60 | $KeepassResults = $KeepassResults | Where-Object { $_.ParentGroup.GetFullPath('/', $true) -notmatch '^[^\/]+?\/Recycle ?Bin$' } 61 | Write-PSFMessage -Level Verbose "Removed $($countBeforeRemoval - $KeepassResults.count) Entries, $countBeforeRemoval entries before and $($KeepassResults.count) after" 62 | } 63 | 64 | # $fullPathes = $KeepassResults|Foreach-Object { 65 | # $path=$_.ParentGroup.GetFullPath('/', $true) 66 | # $title = $_.Strings.ReadSafe('Title') 67 | # "Title= $title; Fullpath= $Path;" 68 | # } 69 | # Write-PSFMessage -level Host -Tag Sherlock "fullPathes=$($fullPathes -join ';')" 70 | if ($KeepassResults.count -gt 1) { 71 | Write-PSFMessage -Level Error "Retrieved $($KeepassResults.count) Keepass-Entries, narrow down the criteria" 72 | return 73 | } 74 | $KeepassEntry = $KeepassResults #[1] 75 | # $KeepassEntry = Get-KPEntry -KeePassConnection $KeepassParams.KeepassConnection -Title $Title 76 | Write-PSFMessage "Found KeepassEntry=$KeepassEntry" -Level Debug 77 | # Write-PSFMessage "`$KeepassEntry.getType()=$($KeepassEntry.GetType())" -tag "Sherlock" 78 | } 79 | catch { 80 | Write-PSFMessage -Level Error "Fehler bei Get-KPEntry, $_" 81 | } 82 | # Write-PSFMessage -Level Warning "Vault ${VaultName}: A secret with the title $Name already exists. This vault currently does not support overwriting secrets. Please remove the secret with Remove-Secret first." 83 | # return $false 84 | 85 | $KPEntry = Set-KPEntry @KeepassParams -Title $Name -PassThru -KeePassEntry $KeepassEntry -Confirm:$False 86 | 87 | # Write-PSFMessage -Level Warning "Vault ${VaultName}: A secret with the title $Name already exists. This vault currently does not support overwriting secrets. Please remove the secret with Remove-Secret first." 88 | # return $false 89 | } 90 | else { 91 | #Set default group 92 | #TODO: Support Creating Secrets with paths 93 | Write-PSFMessage "Adding Keepass Entry" -Target $Name -Tag Add 94 | $KeepassParams.KeePassGroup = (Get-Variable "VAULT_$VaultName").Value.RootGroup 95 | $KPEntry = Add-KPEntry @KeepassParams -Title $Name -PassThru 96 | } 97 | 98 | #Save the changes immediately 99 | #TODO: Consider making this optional as a vault parameter 100 | $KeepassParams.KeepassConnection.Save($null) 101 | 102 | return [Bool]($KPEntry) 103 | } 104 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Test-SecretVault.ps1: -------------------------------------------------------------------------------- 1 | function Test-SecretVault { 2 | [CmdletBinding()] 3 | param ( 4 | [Parameter(ValueFromPipelineByPropertyName,Mandatory)] 5 | [Alias('Vault')][Alias('Name')][string]$VaultName, 6 | 7 | #This intelligent default is here because if you call test-secretvault from other commands it doesn't populate like it does when called from SecretManagement 8 | [Parameter(ValueFromPipelineByPropertyName)] 9 | [Alias('VaultParameters')][hashtable]$AdditionalParameters = (get-secretvault $VaultName).VaultParameters 10 | ) 11 | if ($AdditionalParameters.Verbose) {$VerbosePreference = 'continue'} 12 | 13 | Write-PSFMessage -Level Verbose "SecretManagement: Testing Vault ${VaultName}" 14 | #TODO: Hash vault parameter settings and reset vault state if they change. May be a bug if user changes vault parameters in same session 15 | 16 | #Test if connection already open, no need to do further testing if so 17 | try { 18 | $DBConnection = (Get-Variable -Name "Vault_$VaultName" -Scope Script -ErrorAction Stop).Value 19 | if (-not $DBConnection.isOpen) { 20 | Write-PSFMessage -Level Error 'Connection closed, starting a new connection' 21 | return $false 22 | } 23 | if (Test-DBChanged $DBConnection) { 24 | $dbConnection.close() 25 | Write-PSFMessage -Level Error 'Database file on disk has changed, starting a new connection' 26 | return $false 27 | } 28 | Write-PSFMessage -Level Verbose "Vault ${VaultName}: Connection already open, using existing connection" 29 | return $dbConnection.isOpen 30 | } catch { 31 | Write-PSFMessage -Level Verbose "${VaultName}: $PSItem" 32 | } 33 | 34 | #Basic Sanity Checks 35 | if (-not $VaultName) { 36 | Write-PSFMessage -Level Error 'Keepass: You must specify a Vault Name to test' 37 | return $false 38 | } 39 | 40 | if (-not $AdditionalParameters.Path) { 41 | #TODO: Create a default vault if path isn't supplied 42 | #TODO: Add ThrowUser to throw outside of module scope 43 | Write-PSFMessage -Level Error 'You must specify the Path vault parameter as a path to your KeePass Database' 44 | return $false 45 | } 46 | 47 | if (-not (Test-Path $AdditionalParameters.Path)) { 48 | Write-PSFMessage -Level Error "Could not find the keepass database $($AdditionalParameters.Path). Please verify the file exists or re-register the vault" 49 | return $false 50 | } 51 | 52 | #3 Scenarios Supported: Master PW, Keyfile, PW + Keyfile 53 | $ConnectKPDBParams = @{ 54 | Path = $AdditionalParameters.Path 55 | KeyPath = $AdditionalParameters.KeyPath 56 | UseWindowsAccount = $AdditionalParameters.UseWindowsAccount 57 | UseMasterPassword = $AdditionalParameters.UseMasterPassword 58 | } 59 | 60 | [SecureString]$vaultMasterPassword = Get-Variable -Name "Vault_${VaultName}_MasterPassword" -ValueOnly -ErrorAction SilentlyContinue 61 | if ($vaultMasterPassword) { 62 | Write-PSFMessage -Level Verbose "Cached Master Password Found for $VaultName" 63 | $ConnectKPDBParams.MasterPassword = $vaultMasterPassword 64 | } 65 | 66 | try { 67 | $DBConnection = Connect-KeePassDatabase @ConnectKPDBParams 68 | } catch { 69 | Write-PSFMessage -Level Error $PSItem 70 | } 71 | 72 | 73 | if ($DBConnection.IsOpen) { 74 | Set-Variable -Name "Vault_$VaultName" -Scope Script -Value $DBConnection 75 | return $DBConnection.IsOpen 76 | } 77 | 78 | #If we get this far something went wrong 79 | Write-PSFMessage -Level Error "Unable to open connection to the database" 80 | return $false 81 | 82 | # if (-not $AdditionalParameters.Keypath -or $AdditionalParameters.UseMasterKey) { 83 | 84 | # } 85 | # if (-not (Get-KeePassDatabaseConfiguration -DatabaseProfileName $VaultName)) { 86 | # New-KeePassDatabaseConfiguration @KeePassDBConfigParams 87 | # Write-PSFMessage -Level Verbose "Vault ${VaultName}: A PoshKeePass database configuration was not found but was created." 88 | # return $true 89 | # } 90 | # try { 91 | # Get-KeePassEntry -DatabaseProfileName $VaultName -MasterKey $VaultMasterKey -Title '__SECRETMANAGEMENT__TESTSECRET_SHOULDNOTEXIST' -ErrorAction Stop 92 | # } catch { 93 | # Clear-Variable -Name "Vault_$VaultName" -Scope Script -ErrorAction SilentlyContinue 94 | # throw $PSItem 95 | # } 96 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Unlock-SecretVault.ps1: -------------------------------------------------------------------------------- 1 | function Unlock-SecretVault { 2 | param ( 3 | [Parameter(Mandatory)][SecureString]$Password, 4 | [Parameter(Mandatory)][Alias('Vault')][Alias('Name')][String]$VaultName, 5 | [Alias('VaultParameters')][hashtable]$AdditionalParameters 6 | ) 7 | 8 | Write-PSFMessage "Unlocking SecretVault $VaultName" 9 | $vault = Get-SecretVault -Name $VaultName -ErrorAction Stop 10 | $vaultName = $vault.Name 11 | if ($vault.ModuleName -ne 'SecretManagement.KeePass') { 12 | Write-PSFMessage -Level Error "$vaultName was found but is not a Keepass Vault." 13 | return $false 14 | } 15 | Set-Variable -Name "Vault_${vaultName}_MasterPassword" -Scope Script -Value $Password -Force 16 | #Force a reconnection 17 | Remove-Variable -Name "Vault_${vaultName}" -Scope Script -Force -ErrorAction SilentlyContinue 18 | if (-not (Test-SecretVault -Name $vaultName -AdditionalParameters $AdditionalParameters)) { 19 | Write-PSFMessage -Level Error "${vaultName}: Failed to unlock the vault" 20 | return $false 21 | } 22 | Write-PSFMessage "SecretVault $vault unlocked successfull" 23 | return $true 24 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/Public/Unregister-SecretVault.ps1: -------------------------------------------------------------------------------- 1 | using namespace System.Management.Automation 2 | function Unregister-SecretVault { 3 | [CmdletBinding()] 4 | param( 5 | [string] $VaultName, 6 | [hashtable] $AdditionalParameters 7 | ) 8 | if ($AdditionalParameters.Verbose) {$VerbosePreference = 'continue'} 9 | try { 10 | Remove-Variable -Name "Vault_$VaultName" -Scope Script -Force -ErrorAction Stop 11 | } catch [ItemNotFoundException] { 12 | Write-PSFMessage -Level Verbose "Vault ${VaultName}: Vault was not loaded at time of deregistration" 13 | } 14 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/SecretManagement.KeePass.Extension.psd1: -------------------------------------------------------------------------------- 1 | @{ 2 | ModuleVersion = '0.9.1.3' 3 | RootModule = 'SecretManagement.KeePass.Extension.psm1' 4 | FunctionsToExport = @('Set-Secret','Get-Secret','Remove-Secret','Get-SecretInfo','Test-SecretVault','Unregister-SecretVault','Connect-KeepassDatabase','Unlock-SecretVault') 5 | RequiredModules = @( 6 | @{ ModuleName = 'PSFramework'; ModuleVersion = '1.6.205' } 7 | ) 8 | NestedModules = @( 9 | '../PoshKeePass/PoShKeePass.psd1' 10 | ) 11 | } 12 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.Extension/SecretManagement.KeePass.Extension.psm1: -------------------------------------------------------------------------------- 1 | 2 | using namespace Microsoft.PowerShell.SecretManagement 3 | 4 | Get-ChildItem "$PSScriptRoot/Private" -Exclude "*.Tests.ps1" | Foreach-Object { 5 | . $PSItem.FullName 6 | } 7 | $publicFunctions = Get-ChildItem "$PSScriptRoot/Public" -Exclude "*.Tests.ps1" | Foreach-Object { 8 | . $PSItem.FullName 9 | #Output the name of the function assuming it is the same as the .ps1 file so it can be exported 10 | $PSItem.BaseName 11 | } 12 | 13 | Export-ModuleMember $publicFunctions -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.psd1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/SecretManagement.KeePass.psd1 -------------------------------------------------------------------------------- /SecretManagement.KeePass/SecretManagement.KeePass.psm1: -------------------------------------------------------------------------------- 1 | #region SourceInit 2 | $publicFunctions = Get-ChildItem "$PSScriptRoot/Public" -Exclude "*.Tests.ps1" | Foreach-Object { 3 | . $PSItem.FullName 4 | #Output the name of the function assuming it is the same as the .ps1 file so it can be exported 5 | $PSItem.BaseName 6 | } 7 | 8 | Export-ModuleMember $publicFunctions 9 | #endregion SourceInit -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Get-Secret.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe 'Get-Secret' { 2 | BeforeAll { 3 | #Setup Testing Environment and mock calls to/from parent SecretManagement Module 4 | 5 | #Remove SecretManagement Parent Module if Present 6 | Get-Module 'SecretManagement.KeePass' | Remove-Module -Force 7 | Get-Module 'Microsoft.Powershell.SecretManagement' | Remove-Module -Force 8 | 9 | $ExtensionModule = Import-Module "$PSScriptRoot/../SecretManagement.KeePass.Extension/*.psd1" -Force -PassThru 10 | $Mocks = Join-Path $PSScriptRoot './Mocks' | Resolve-Path 11 | $BaseKeepassDatabaseName = 'Testdb' 12 | $DoubleEntryExceptionMessage = 'Multiple ambiguous entries found for double entry, please remove the duplicate entry or specify the full path of the secret' 13 | $ExtModuleName = $ExtensionModule.Name 14 | 15 | Mock -ModuleName $ExtModuleName 'Get-SecretVault' { 16 | @{ 17 | VaultName = $VaultName 18 | VaultParameters = @{ 19 | Path = $vaultPath 20 | } 21 | } 22 | } 23 | } 24 | AfterAll { 25 | Remove-Module $ExtensionModule -Force 26 | } 27 | 28 | Context 'Function Parameter Validation' { 29 | BeforeAll { 30 | $SCRIPT:FunctionName = 'Get-Secret' 31 | } 32 | It 'has one parameter set' { 33 | (Get-Command -Module $ExtModuleName -Name $FunctionName).ParameterSets.Count | Should -BeExactly 1 34 | } 35 | It 'has a parameter ""' { 36 | $allParameterNames = (Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters.Keys 37 | $Name | Should -BeIn $AllParameterNames 38 | } -TestCases @( 39 | @{Name = 'Name' } 40 | @{Name = 'VaultName' } 41 | @{Name = 'AdditionalParameters' } 42 | ) 43 | 44 | It 'has the mandatory value of parameter "" set to ""' { 45 | $testAttribute = ((Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters[$Name].Attributes | 46 | Where-Object { $PSItem -is [System.Management.Automation.ParameterAttribute] }).Mandatory 47 | $testAttribute | Should -Be $Mandatory 48 | } -TestCases @( 49 | @{Name = 'Name'; Mandatory = $False } 50 | @{Name = 'VaultName'; Mandatory = $False } 51 | @{Name = 'AdditionalParameters'; Mandatory = $False } 52 | ) 53 | 54 | It 'has parameter of type ' { 55 | ((Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters[$Name].ParameterType) | 56 | Should -BeExactly $Type 57 | } -TestCases @( 58 | @{Name = 'Name'; Type = 'string' } 59 | @{Name = 'VaultName'; Type = 'string' } 60 | @{Name = 'AdditionalParameters'; Type = 'hashtable' } 61 | ) 62 | } 63 | 64 | Context 'Get Secret information from MasterPassword protected KeePass' { 65 | BeforeAll { 66 | $masterKey = '"1}`.2R{LX1`Jm8%XX2/' 67 | $vaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $masterKey)) 68 | 69 | $vaultName = "KeepassPesterTest_$([guid]::NewGuid())" 70 | $keePassDatabaseSuffix = 'PathOnly' 71 | $keePassDatabaseFileName = "$($baseKeepassDatabaseName)$($keePassDatabaseSuffix).kdbx" 72 | $vaultPath = Join-Path -Path $TestDrive -ChildPath $keePassDatabaseFileName 73 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 74 | 75 | $vaultParams = @{ 76 | VaultName = $VaultName 77 | VaultParameters = @{ 78 | Path = $vaultPath 79 | } 80 | } 81 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 82 | } 83 | 84 | It 'should return a for entry ' -Tag CurrentTest { 85 | $Secret = Get-Secret @vaultParams -Name $SecretName 86 | $Secret | Should -BeOfType $PSType 87 | } -TestCases @( 88 | @{SecretName = 'New Entry 1';PSType = 'System.Management.Automation.PSCredential' } 89 | @{SecretName = 'New Entry 2';PSType = 'System.Management.Automation.PSCredential' } 90 | @{SecretName = 'No UserName';PSType = 'System.Security.SecureString' } 91 | ) 92 | 93 | It 'should return for ' { 94 | $getSecretResult = Get-Secret @vaultParams -Name $SecretName 95 | $getSecretResult.UserName | Should -BeExactly $UserName 96 | } -TestCases @( 97 | @{SecretName = 'New Entry 1';UserName = 'myusername 1' } 98 | @{SecretName = 'New Entry 2';UserName = 'Some Administrator account' } 99 | ) 100 | 101 | It 'should throw when multiple secrets are returned' { 102 | { Get-Secret @vaultParams -Name 'double entry' -ErrorAction Stop } | 103 | Should -Throw -ExpectedMessage $DoubleEntryExceptionMessage 104 | } 105 | It 'should return nothing when entry is not found in the KeePass DB' { 106 | Get-Secret @vaultParams -Name 'not present' | Should -BeNullOrEmpty 107 | } 108 | } 109 | 110 | Context 'Get Secret information from KeyFile protected KeePass' { 111 | BeforeAll { 112 | $KeyFileName = 'TestdbKeyFile.key' 113 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 114 | $KeePassDatabaseSuffix = 'KeyFile' 115 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 116 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 117 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 118 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 119 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 120 | 121 | $vaultParams = @{ 122 | VaultName = $VaultName 123 | VaultParameters = @{ 124 | Path = $VaultPath 125 | KeyPath = $KeyPath 126 | } 127 | } 128 | } 129 | 130 | It 'should return a for entry ' { 131 | $Secret = Get-Secret @vaultParams -Name $SecretName 132 | $Secret | Should -Not -BeNullOrEmpty 133 | $Secret | Should -BeOfType $PSType 134 | } -TestCases @( 135 | @{SecretName = 'New Entry 1';PSType = 'System.Management.Automation.PSCredential' } 136 | @{SecretName = 'New Entry 2';PSType = 'System.Management.Automation.PSCredential' } 137 | @{SecretName = 'No UserName';PSType = 'System.Security.SecureString' } 138 | ) 139 | 140 | It 'should return for ' { 141 | $secretResult = Get-Secret @vaultParams -Name $SecretName 142 | $secretResult.UserName | Should -BeExactly $UserName 143 | } -TestCases @( 144 | @{SecretName = 'New Entry 1';UserName = 'myusername 1' } 145 | @{SecretName = 'New Entry 2';UserName = 'Some Administrator account' } 146 | ) 147 | 148 | It 'should throw when multiple secrets are returned' { 149 | { Get-Secret @vaultParams -Name 'double entry' -ErrorAction Stop } | 150 | Should -Throw -ExpectedMessage $DoubleEntryExceptionMessage 151 | } 152 | It 'should return nothing when entry is not found in the KeePass DB' { 153 | Get-Secret @vaultParams -Name 'not present' | 154 | Should -BeNullOrEmpty 155 | } 156 | } 157 | 158 | Context 'Get Secret information from MasterPassword and KeyFile protected KeePass' { 159 | BeforeAll { 160 | $KeyFileName = 'TestdbKeyFileAndMasterPassword.key' 161 | $MasterKey = '"1}`.2R{LX1`Jm8%XX2/' 162 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 163 | 164 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 165 | $KeePassDatabaseSuffix = 'KeyFileAndMasterPassword' 166 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 167 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 168 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 169 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 170 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 171 | 172 | $vaultParams = @{ 173 | VaultName = $VaultName 174 | VaultParameters = @{ 175 | Path = $VaultPath 176 | UseMasterPassword = $true 177 | KeyPath = $KeyPath 178 | } 179 | } 180 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 181 | } 182 | 183 | It 'should return a for entry ' { 184 | $Secret = Get-Secret @vaultParams -Name $SecretName 185 | $Secret | Should -BeOfType $PSType 186 | } -TestCases @( 187 | @{SecretName = 'New Entry 1';PSType = 'System.Management.Automation.PSCredential' } 188 | @{SecretName = 'New Entry 2';PSType = 'System.Management.Automation.PSCredential' } 189 | @{SecretName = 'No UserName';PSType = 'System.Security.SecureString' } 190 | ) 191 | 192 | It 'should return for ' { 193 | $getSecretResult = Get-Secret @vaultParams -Name $SecretName 194 | $getSecretResult.UserName | Should -BeExactly $UserName 195 | } -TestCases @( 196 | @{SecretName = 'New Entry 1';UserName = 'myusername 1' } 197 | @{SecretName = 'New Entry 2';UserName = 'Some Administrator account' } 198 | ) 199 | 200 | It 'should throw when multiple secrets are returned' { 201 | { Get-Secret @vaultParams -Name 'double entry' -ErrorAction Stop } | 202 | Should -Throw -ExpectedMessage $DoubleEntryExceptionMessage 203 | } 204 | 205 | It 'should return nothing when entry is not found in the KeePass DB' { 206 | Get-Secret @vaultParams -Name 'not present' | Should -BeNullOrEmpty 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbKeyFile.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/Tests/Mocks/TestdbKeyFile.kdbx -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbKeyFile.key: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.00 5 | 6 | 7 | 80NRqIoAD9U5kTDIiWbu5szvxj/9vB3cSTmFfwArZvk= 8 | 9 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbKeyFileAndMasterPassword.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/Tests/Mocks/TestdbKeyFileAndMasterPassword.kdbx -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbKeyFileAndMasterPassword.key: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 1.00 5 | 6 | 7 | hzo9VQ6TpenHQbK28kwcm5Tgi0g66MMujrbfDgxwo2E= 8 | 9 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbKeyFileV2.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/Tests/Mocks/TestdbKeyFileV2.kdbx -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbKeyFileV2.keyx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 2.0 5 | 6 | 7 | 8 | 0176FB93 276FE575 9CCB2938 B26A24E4 9 | 22162891 A5229055 BCD5B7B1 9A741851 10 | 11 | 12 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbPathAndUseMasterPassword.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/Tests/Mocks/TestdbPathAndUseMasterPassword.kdbx -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Mocks/TestdbPathOnly.kdbx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/SecretManagement.KeePass/Tests/Mocks/TestdbPathOnly.kdbx -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Register-KeePassSecretVault.Tests.ps1: -------------------------------------------------------------------------------- 1 | 2 | Describe 'Register-KeepassSecretVault' { 3 | BeforeAll { 4 | Import-Module "$PSScriptRoot/../SecretManagement.KeePass.psd1" -Force 5 | $SCRIPT:Mocks = Resolve-Path "$PSScriptRoot/Mocks" 6 | $SCRIPT:TestDB = Join-Path $Mocks 'TestdbKeyFile.kdbx' 7 | $SCRIPT:TestDBKey = Join-Path $Mocks 'TestdbKeyFile.key' 8 | $SCRIPT:TestDBName = ([io.fileinfo]$TestDB).Basename 9 | Unregister-SecretVault -Name $TestDBName -ErrorAction SilentlyContinue 10 | } 11 | AfterEach { 12 | Unregister-SecretVault -Name $TestDBName -ErrorAction SilentlyContinue 13 | if (-not $testdrive) {throw 'TestDrive Missing! This should not happen, bailing out for safety.'} 14 | Get-ChildItem $testdrive | Remove-Item -Force 15 | } 16 | AfterAll { 17 | Remove-Module SecretManagement.KeePass 18 | } 19 | 20 | It 'Registers a Vault' { 21 | Register-KeepassSecretVault -ErrorAction Stop -Path $TestDB -KeyPath $TestDBKey 22 | Get-SecretVault -Name $TestDBName -OutVariable myvault | Should -not -BeNullOrEmpty 23 | $myvault.Name | Should -Be $TestDBName 24 | $myvault.ModuleName | Should -Be 'SecretManagement.KeePass' 25 | $myVault.VaultParameters.UseMasterPassword | Should -BeFalse 26 | $myVault.VaultParameters.UseWindowsAccount | Should -BeFalse 27 | $myVault.VaultParameters.Path | Should -Be $TestDB 28 | $myVault.VaultParameters.KeyPath | Should -Be $TestDBKey 29 | } 30 | It 'Fails if bad path specified' { 31 | {Register-KeepassSecretVault -ErrorAction Stop -Path "$HOME\Path\To\Nowhere.kdbx"} | 32 | Should -Throw -ErrorId 'PathNotFound,Microsoft.PowerShell.Commands.ResolvePathCommand' 33 | } 34 | It 'Fails if no auth method specified' { 35 | {Register-KeepassSecretVault -ErrorAction Stop -Path $TestDB} | 36 | Should -Throw 'No authentication methods specified*' 37 | } 38 | 39 | It 'Creates a new vault if Create is specified' { 40 | $RegisterParams = @{ 41 | Create = $true 42 | Path = (Join-Path $TestDrive "$TestDBName.kdbx") 43 | KeyPath = (Join-Path $TestDrive "$TestDBName.key") 44 | } 45 | 46 | Register-KeePassSecretVault @RegisterParams 47 | 48 | Get-SecretVault -Name $TestDBName -OutVariable DB | Should -Not -BeNullOrEmpty 49 | $expectedVaultParameters = @{ 50 | Path = $RegisterParams.Path 51 | KeyPath = $RegisterParams.KeyPath 52 | UseMasterPassword = 'False' 53 | UseWindowsAccount = 'False' 54 | } 55 | 56 | $expectedVaultParameters.keys.foreach{ 57 | $DB.VaultParameters.$PSItem | Should -Be $expectedVaultParameters.$PSItem 58 | } 59 | } 60 | 61 | It 'Doesnt Clobber an existing vault if Create is specified' { 62 | $RegisterParams = @{ 63 | Create = $true 64 | Path = (Join-Path $TestDrive "$TestDBName.kdbx") 65 | KeyPath = (Join-Path $TestDrive "$TestDBName.key") 66 | } 67 | 68 | #Simulate an already present vault 69 | New-Item $RegisterParams.Path 70 | 71 | {Register-KeePassSecretVault @RegisterParams} | 72 | Should -Throw '-Create was specified but a database already exists*' 73 | } 74 | It 'Doesnt Clobber an existing keyfile if Create is specified' { 75 | $RegisterParams = @{ 76 | Create = $true 77 | Path = (Join-Path $TestDrive "$TestDBName.kdbx") 78 | KeyPath = $TestDBKey 79 | } 80 | $dbKeyHash = (Get-FileHash $TestDBKey).Hash 81 | $dbKeyDateModified = (Get-Item $TestDBKey).LastWriteTime 82 | Register-KeePassSecretVault @RegisterParams 83 | (Get-FileHash $TestDBKey).Hash | Should -Be $dbKeyHash 84 | (Get-Item $TestDBKey).LastWriteTime | Should -Be $dbKeyDateModified 85 | } 86 | It 'Uses full titles if showfulltitle is specified' { 87 | Register-KeepassSecretVault -ErrorAction Stop -Path $TestDB -KeyPath $TestDBKey -ShowFullTitle 88 | (Get-SecretInfo 'General/New Entry 1' -Vault $TestDBName 3>$null).Name | Should -Be 'General/New Entry 1' 89 | } 90 | It 'Succeeds with bad path but SkipValidate specified' { 91 | Register-KeePassSecretVault -Name $TestDBName -Path "$TestDrive/NotArealPath" -SkipValidate -KeyPath "$TestDrive/NotARealKey" 92 | Get-SecretVault -Name $TestDBName | Should -Not -BeNullOrEmpty 93 | } 94 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Remove-Secret.Tests.ps1: -------------------------------------------------------------------------------- 1 | Describe 'Remove-Secret' { 2 | BeforeAll { 3 | #Setup Testing Environment and mock calls to/from parent SecretManagement Module 4 | #Remove SecretManagement Parent Module if Present 5 | Get-Module 'SecretManagement.KeePass' | Remove-Module -Force 6 | Get-Module 'Microsoft.Powershell.SecretManagement' | Remove-Module -Force 7 | 8 | $ExtensionModule = Import-Module "$PSScriptRoot/../SecretManagement.KeePass.Extension/*.psd1" -Force -PassThru 9 | $Mocks = Join-Path $PSScriptRoot './Mocks' | Resolve-Path 10 | $BaseKeepassDatabaseName = 'Testdb' 11 | $DoubleEntryExceptionMessage = 'Multiple ambiguous entries found for double entry, please remove the duplicate entry or specify the full path of the secret' 12 | $ExtModuleName = $ExtensionModule.Name 13 | 14 | Mock -ModuleName $ExtModuleName 'Get-SecretVault' { 15 | @{ 16 | VaultName = $VaultName 17 | VaultParameters = @{ 18 | Path = $vaultPath 19 | } 20 | } 21 | } 22 | } 23 | BeforeEach { 24 | $BaseKeepassDatabaseName = 'Testdb' 25 | $ModulePath = (Resolve-Path $PSScriptRoot/../..) 26 | $MasterKey = '"1}`.2R{LX1`Jm8%XX2/' 27 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 28 | 29 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 30 | $KeePassDatabaseSuffix = 'PathOnly' 31 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 32 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 33 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 34 | $vaultParams = @{ 35 | VaultName = $VaultName 36 | VaultParameters = @{ 37 | Path = $vaultPath 38 | } 39 | } 40 | Mock -Verifiable -ModuleName $ExtModuleName 'Get-Credential' -MockWith { $VaultMasterKey } 41 | 42 | #Create one test key to remove 43 | $TestSecretName = 'PesterTestSecret' 44 | Set-Secret @vaultParams -Name $TestSecretName -Secret 'supersafe' 45 | $TestSecretParams = @{ 46 | Vault = $VaultName 47 | Name = $TestSecretName 48 | } 49 | } 50 | 51 | It 'Fails if name not specified' { 52 | { Remove-Secret @vaultParams -Name $null } | 53 | Should -Throw -ErrorId 'ParameterArgumentValidationError*' 54 | } 55 | It 'Removes predefined secret' { 56 | Remove-Secret @vaultParams -Name $TestSecretName 57 | Get-SecretInfo @TestSecretParams | Should -BeNullOrEmpty 58 | } 59 | It 'Fails on removing already removed secret' { 60 | Remove-Secret @vaultParams -Name $TestSecretName 61 | { 62 | Remove-Secret @vaultParams -Name $TestSecretName -ErrorVariable err 2>$null 63 | } | Should -Throw "Vault * No Keepass Entry named $TestSecretName found" 64 | } 65 | It 'Fails on duplicate secrets' { 66 | { 67 | Remove-Secret @vaultParams -Name 'Double Entry' -ErrorVariable err 2>$null 68 | } | Should -Throw 'Vault * There are multiple entries*' 69 | } 70 | } -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/SecretManagementVault.Tests.ps1: -------------------------------------------------------------------------------- 1 | #requires -modules @{ModuleName="Pester"; ModuleVersion="5.1.0"} 2 | Describe 'SecretManagement.Keepass' { 3 | BeforeAll { 4 | Remove-Module Microsoft.Powershell.SecretManagement, SecretManagement.Keepass, SecretManagement.KeePass.Extension -ErrorAction SilentlyContinue -Force 5 | 6 | #Fetch helper function 7 | . $PSScriptRoot/../SecretManagement.KeePass.Extension/Private/Unlock-SecureString.ps1 8 | 9 | #Would use TestDrive but the PoshKeePass Module doesn't understand it for purposes of new-keepassdatabase 10 | $VaultName = 'SecretManagement.Tests' 11 | $VaultExtensionName = 'SecretManagement.KeePass' 12 | $VaultPath = Join-Path $TestDrive.FullName 'KeepassTestVault.kdbx' 13 | $SCRIPT:VaultKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force 'ThisIsATestVaultYouShouldNotUseIt')) 14 | 15 | Import-Module "$PSScriptRoot/../PoshKeePass/PoShKeePass.psd1" -Force 16 | 17 | #Create three variations of databases: Master Key only, keyfile, and both 18 | $VaultKeyFilePath = Join-Path $TestDrive.FullName 'KeepassTestKeyFileVault.key' 19 | $VaultKeyDBPath = $VaultPath -replace 'Vault', 'KeyVault' 20 | $VaultKeyPWDBPath = $VaultPath -replace 'Vault', 'KeyPWVault' 21 | [KeePassLib.Keys.KcpKeyFile]::Create($VaultKeyFilePath, $null) 22 | New-KeePassDatabase -DatabasePath $VaultPath -MasterKey $VaultKey 23 | New-KeePassDatabase -DatabasePath $VaultKeyDBPath -KeyPath $VaultKeyFilePath 24 | New-KeePassDatabase -DatabasePath $VaultKeyPWDBPath -KeyPath $VaultKeyFilePath -MasterKey $VaultKey 25 | 26 | Remove-Module PoshKeePass 27 | 28 | Import-Module "$PSScriptRoot/../SecretManagement.KeePass.psd1" -Force 29 | 30 | $SCRIPT:RegisterSecretVaultParams = @{ 31 | Name = $VaultName 32 | ModuleName = (Get-Module $VaultExtensionName).Path 33 | PassThru = $true 34 | VaultParameters = @{ 35 | Path = $VaultPath 36 | } 37 | } 38 | try { 39 | $SCRIPT:TestVault = Register-SecretVault @RegisterSecretVaultParams 40 | } 41 | catch [InvalidOperationException] { 42 | if ($PSItem -match 'Provided Name for vault is already being used') { 43 | Unregister-SecretVault -Name $RegisterSecretVaultParams.Name 44 | $SCRIPT:TestVault = Register-SecretVault @RegisterSecretVaultParams 45 | } 46 | else { 47 | $PSCmdlet.ThrowTerminatingError($PSItem) 48 | } 49 | } 50 | } 51 | 52 | AfterAll { 53 | $TestVault | Unregister-SecretVault -ErrorAction SilentlyContinue 54 | Get-Item $VaultPath -ErrorAction SilentlyContinue | Remove-Item 55 | } 56 | 57 | BeforeEach { 58 | $secretName = "tests/$((New-Guid).Guid)" 59 | } 60 | 61 | Context 'Unlock' { 62 | It 'Unattended Vault Unlock' { 63 | Unlock-SecretVault -Name $TestVault.Name -Password $VaultKey.Password 64 | Test-SecretVault -Name $TestVault.Name | Should -Be $true 65 | } 66 | } 67 | 68 | Context 'InvalidRegistration' { 69 | BeforeAll { 70 | $SCRIPT:InvalidVaultName = 'Pester.InvalidVault' 71 | Register-SecretVault -Name $InvalidVaultName -ModuleName (Resolve-Path $PSScriptRoot/..) -VaultParameters @{Path = "$TestDrive\NotARealDB.kdbx" } 72 | } 73 | AfterAll { 74 | Unregister-SecretVault -Name $InvalidVaultName 75 | } 76 | It 'Test-SecretVault should fail on uninitalized vault' { 77 | Test-SecretVault -Name $InvalidVaultName -ErrorVariable mytest 2>$null | Should -Be $False 78 | } 79 | } 80 | 81 | Context 'SecretManagement' { 82 | BeforeAll { 83 | #Unlock the vault 84 | Test-SecretVault -Name $TestVault.Name 85 | } 86 | 87 | It 'Get-SecretVault' { 88 | Get-SecretVault -Name $TestVault.Name | Should -Not -BeNullOrEmpty 89 | } 90 | It 'Test-SecretVault' { 91 | Test-SecretVault -Name $TestVault.Name | Should -Be $true 92 | } 93 | 94 | It 'Get/Set/Remove String' { 95 | $secretText = 'This is my string secret' 96 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secretText 97 | $secretInfo = Get-SecretInfo -Name $secretName -Vault $VaultName 98 | $secretInfo.Name | Should -BeExactly $secretName 99 | $secretInfo.VaultName | Should -BeExactly $VaultName 100 | 101 | #Metadata 102 | $secretInfo.Metadata.IconName | Should -Be 'Key' 103 | $secretInfo.Metadata.ParentGroup | Should -Be 'KeePassTestVault' 104 | 105 | $secret = Get-Secret -Name $secretName -Vault $VaultName 106 | $secret | Should -Be 'System.Security.SecureString' 107 | Unlock-SecureString $secret | Should -BeExactly $secretText 108 | 109 | Remove-Secret -Name $secretName -Vault $VaultName 110 | { 111 | Get-Secret -Name $secretName -Vault $VaultName -ErrorAction Stop 112 | } | Should -Throw -ErrorId 'GetSecretNotFound,Microsoft.PowerShell.SecretManagement.GetSecretCommand' 113 | } 114 | 115 | It 'Get/Set/Remove SecureString' { 116 | $secretText = 'This is my securestring secret' 117 | Set-Secret -Name $secretName -Vault $VaultName -Secret ($secretText | ConvertTo-SecureString -AsPlainText -Force) 118 | 119 | $secretInfo = Get-SecretInfo -Name $secretName -Vault $VaultName 120 | $secretInfo.Name | Should -BeExactly $secretName 121 | $secretInfo.VaultName | Should -BeExactly $VaultName 122 | 123 | $secret = Get-Secret -Name $secretName -AsPlainText -Vault $VaultName 124 | $secret | Should -BeExactly $secretText 125 | 126 | Remove-Secret -Name $secretName -Vault $VaultName 127 | { Get-Secret -Name $secretName -Vault $VaultName -ErrorAction Stop } | Should -Throw -ErrorId 'GetSecretNotFound,Microsoft.PowerShell.SecretManagement.GetSecretCommand' 128 | } 129 | 130 | It 'Get/Set/Remove PSCredential' { 131 | $secretPassword = 'PesterPassword' 132 | $secret = [PSCredential]::new('PesterUser', ($secretPassword | ConvertTo-SecureString -AsPlainText -Force)) 133 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secret 134 | $secretInfo = Get-SecretInfo -Name $secretName -Vault $VaultName 135 | $secretInfo.Name | Should -BeLike $secretName 136 | $secretInfo.VaultName | Should -BeExactly $VaultName 137 | $storedSecret = Get-Secret -Name $secretName -Vault $VaultName 138 | $storedSecret | Should -BeOfType [PSCredential] 139 | $storedSecret.GetNetworkCredential().Password | Should -BeExactly $secretPassword 140 | $storedSecret.Username | Should -BeExactly $secret.UserName 141 | Remove-Secret -Name $secretName -Vault $VaultName 142 | { 143 | Get-Secret -Name $secretName -Vault $VaultName -ErrorAction Stop 144 | } | Should -Throw -ErrorId 'GetSecretNotFound,Microsoft.PowerShell.SecretManagement.GetSecretCommand' 145 | } 146 | 147 | It 'Should not create a duplicate entry with Set-Secret' { 148 | Set-ItResult -Skipped -Because 'Broken by 1.1.0 - https://github.com/PowerShell/SecretManagement/issues/151' 149 | $secretPassword = 'PesterPassword' 150 | $secret = [PSCredential]::new('PesterUser', ($secretPassword | ConvertTo-SecureString -AsPlainText -Force)) 151 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secret 152 | [String]$DuplicateSecretWarning = Set-Secret -Name $secretName -Vault $VaultName -Secret $secret -WarningAction Continue *>&1 153 | [String]$DuplicateSecretWarning | Should -BeLike "*A secret with the title $secretName already exists*" 154 | } 155 | It 'Should update an existing entry with Set-Secret' { 156 | # Set-ItResult -Skipped -Because 'Broken by 1.1.0 - https://github.com/PowerShell/SecretManagement/issues/151' 157 | $secretName = "New-Secret ToBeUpdated" 158 | $secretPassword = 'PesterPassword' 159 | $secretPasswordAfterUpdate = 'PesterPasswordWasUpdated' 160 | $secret = [PSCredential]::new('PesterUser', ($secretPassword | ConvertTo-SecureString -AsPlainText -Force)) 161 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secret 162 | Get-Secret -Name $secretName -Vault $VaultName | Should -Not -BeNullOrEmpty 163 | 164 | $secret = [PSCredential]::new('PesterUser', ($secretPasswordAfterUpdate | ConvertTo-SecureString -AsPlainText -Force)) 165 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secret 166 | $secretAfterUpdate = Get-Secret -Name $secretName -Vault $VaultName 167 | $secretAfterUpdate | Should -Not -BeNullOrEmpty 168 | Write-PSFMessage "`$secretAfterUpdate=$($secretAfterUpdate.Username):$($secretAfterUpdate.GetNetworkCredential().password)" 169 | $secretAfterUpdate.GetNetworkCredential().password | Should -Be $secretPasswordAfterUpdate 170 | } 171 | It 'Search a non existing entry' { 172 | { Get-Secret -Name "ThisOneDoesNotExist" -Vault $VaultName -ErrorAction stop } | Should -Throw -ErrorId 'GetSecretNotFound,Microsoft.PowerShell.SecretManagement.GetSecretCommand' 173 | } 174 | It 'Should ignore recycle bin on update of an existing entry with Set-Secret' { 175 | $functionName = 'Should ignore recycle bin on update of an existing entry with Set-Secret' 176 | $secretPassword = 'PesterPassword' 177 | $secretPasswordAfterUpdate = 'PesterPasswordWasUpdated' 178 | $secret = [PSCredential]::new('PesterUser', ($secretPassword | ConvertTo-SecureString -AsPlainText -Force)) 179 | # Set a secret, verify it 180 | Write-PSFMessage "Set a secret, verify it" -FunctionName $functionName 181 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secret 182 | Get-Secret -Name $secretName -Vault $VaultName | Should -Not -BeNullOrEmpty 183 | 184 | # remove it, verify it 185 | Write-PSFMessage "remove it, verify it" -FunctionName $functionName 186 | Remove-Secret -Name $secretName -Vault $VaultName 187 | { Get-Secret -Name $secretName -Vault $VaultName -ErrorAction stop } | Should -Throw -ErrorId 'GetSecretNotFound,Microsoft.PowerShell.SecretManagement.GetSecretCommand' 188 | 189 | # Create it again, verify it 190 | Write-PSFMessage "Create it again, verify it" -FunctionName $functionName 191 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secret 192 | Get-Secret -Name $secretName -Vault $VaultName | Should -Not -BeNullOrEmpty 193 | 194 | # Update the new Credential 195 | Write-PSFMessage "Update the new Credential, even if one with the same name exists in the recycle bin" -FunctionName $functionName 196 | $secret = [PSCredential]::new('PesterUser', ($secretPasswordAfterUpdate | ConvertTo-SecureString -AsPlainText -Force)) 197 | Set-Secret -Name $secretName -Vault $VaultName -Secret $secret 198 | $secretAfterUpdate = Get-Secret -Name $secretName -Vault $VaultName 199 | $secretAfterUpdate | Should -Not -BeNullOrEmpty 200 | # Write-PSFMessage "`$secretAfterUpdate=$($secretAfterUpdate.Username):$($secretAfterUpdate.GetNetworkCredential().password)" 201 | $secretAfterUpdate.GetNetworkCredential().password | Should -Be $secretPasswordAfterUpdate 202 | } 203 | 204 | It 'Register-SecretVault -AllowClobber' { 205 | $RegisterSecretVaultParams.VaultParameters.Pester = $true 206 | $RegisterSecretVaultParams.AllowClobber = $true 207 | $newVault = Register-SecretVault @RegisterSecretVaultParams 208 | $newVault.VaultParameters.Pester | Should -BeTrue 209 | } 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/Test-SecretVault.Tests.ps1: -------------------------------------------------------------------------------- 1 | $CommonTests = Join-Path $PSScriptRoot './TestSecretVault-CommonTests.include.ps1' 2 | # Attention: 3 | # Test will fail under Windows Powershell 5.1 if the localization is not en-US 4 | Describe 'Test-SecretVault' { 5 | BeforeAll { 6 | #Setup Testing Environment and mock calls to/from parent SecretManagement Module 7 | #Remove SecretManagement Parent Module if Present 8 | Get-Module 'SecretManagement.KeePass' | Remove-Module -Force 9 | Get-Module 'Microsoft.Powershell.SecretManagement' | Remove-Module -Force 10 | 11 | $ExtensionModule = Import-Module "$PSScriptRoot/../SecretManagement.KeePass.Extension/*.psd1" -Force -PassThru 12 | $Mocks = Join-Path $PSScriptRoot './Mocks' | Resolve-Path 13 | 14 | $BaseKeepassDatabaseName = 'Testdb' 15 | $ExtModuleName = $ExtensionModule.Name 16 | $DoubleEntryExceptionMessage = 'Multiple ambiguous entries found for double entry, please remove the duplicate entry or specify the full path of the secret' 17 | $KeePassCompositeError = '*The composite key is invalid!*Make sure the composite key is correct and try again.*' 18 | $KeePassMasterKeyError = '*The master key is invalid!*' 19 | 20 | Mock -ModuleName $ExtModuleName 'Get-SecretVault' { 21 | @{ 22 | VaultName = $VaultName 23 | VaultParameters = @{ 24 | Path = $vaultPath 25 | } 26 | } 27 | } 28 | } 29 | 30 | Context 'Function Parameter Validation' { 31 | BeforeAll { 32 | $ExtModuleName = $ExtensionModule.Name 33 | $FunctionName = 'Test-SecretVault' 34 | $ParameterCount = 2 35 | } 36 | 37 | It 'has a parameter vaultname' { 38 | $Name = 'vaultname' 39 | $AllParameterNames = (Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters.Keys 40 | $Name | Should -BeIn $AllParameterNames 41 | } 42 | It 'has a parameter ""' -TestCases @( 43 | @{Name = 'VaultName' } 44 | @{Name = 'AdditionalParameters' } 45 | ) { 46 | $AllParameterNames = (Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters.Keys 47 | $Name | Should -BeIn $AllParameterNames 48 | } 49 | It 'has the mandatory value of parameter "" set to ""' -TestCases @( 50 | @{Name = 'VaultName'; Mandatory = $True } 51 | @{Name = 'AdditionalParameters'; Mandatory = $False } 52 | ) { 53 | ((Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters[$Name].Attributes | Where-Object { $_.GetType().FullName -eq 'System.Management.Automation.ParameterAttribute' }).Mandatory | Should -Be $Mandatory 54 | } 55 | It 'has parameter of type ' -TestCases @( 56 | @{Name = 'VaultName'; Type = 'string' } 57 | @{Name = 'AdditionalParameters'; Type = 'hashtable' } 58 | ) { 59 | ((Get-Command -Module $ExtModuleName -Name $FunctionName).Parameters[$Name].ParameterType) | Should -BeExactly $Type 60 | } 61 | It 'has one parameter set' { 62 | (Get-Command -Module $ExtModuleName -Name $FunctionName).ParameterSets.Count | Should -BeExactly 1 63 | } 64 | } 65 | 66 | Context 'Validating with correct MasterPassword' { 67 | BeforeAll { 68 | $MasterKey = '"1}`.2R{LX1`Jm8%XX2/' 69 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 70 | $KeePassDatabaseSuffix = 'PathOnly' 71 | 72 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 73 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 74 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 75 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 76 | 77 | $vaultParams = @{ 78 | VaultName = $VaultName 79 | VaultParameters = @{ 80 | Path = $vaultPath 81 | } 82 | } 83 | 84 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 85 | } 86 | 87 | . $CommonTests -Credential 88 | } 89 | 90 | Context 'Validating with incorrect MasterPassword' { 91 | BeforeAll { 92 | $MasterKey = 'ThisIsAnInvalidMasterKey' 93 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 94 | 95 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 96 | $KeePassDatabaseSuffix = 'PathOnly' 97 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 98 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 99 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 100 | 101 | $vaultParams = @{ 102 | VaultName = $VaultName 103 | VaultParameters = @{ 104 | Path = $vaultPath 105 | } 106 | } 107 | 108 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 109 | } 110 | . $CommonTests -Invalid -Credential 111 | } 112 | 113 | Context 'Validating with Path and correct UseMasterPassword' { 114 | BeforeAll { 115 | $MasterKey = '"1}`.2R{LX1`Jm8%XX2/' 116 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 117 | 118 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 119 | $KeePassDatabaseSuffix = 'PathAndUseMasterPassword' 120 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 121 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 122 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 123 | 124 | $vaultParams = @{ 125 | VaultName = $VaultName 126 | VaultParameters = @{ 127 | Path = $vaultPath 128 | UseMasterPassword = $true 129 | } 130 | } 131 | 132 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 133 | } 134 | . $CommonTests -Credential 135 | } 136 | Context 'Validating with path and incorrect MasterPassword' { 137 | BeforeAll { 138 | $MasterKey = 'ThisIsAnInvalidMasterKey' 139 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 140 | 141 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 142 | $KeePassDatabaseSuffix = 'PathOnly' 143 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 144 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 145 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 146 | 147 | $vaultParams = @{ 148 | VaultName = $VaultName 149 | VaultParameters = @{ 150 | Path = $vaultPath 151 | UseMasterPassword = $true 152 | } 153 | } 154 | 155 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 156 | } 157 | . $CommonTests -Invalid -Credential 158 | } 159 | 160 | Context 'Validating with correct Keyfile' { 161 | BeforeAll { 162 | $KeyFileName = 'TestdbKeyFile.key' 163 | 164 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 165 | $KeePassDatabaseSuffix = 'KeyFile' 166 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 167 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 168 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 169 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 170 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 171 | 172 | $vaultParams = @{ 173 | VaultName = $VaultName 174 | VaultParameters = @{ 175 | Path = $vaultPath 176 | KeyPath = $KeyPath 177 | } 178 | } 179 | } 180 | . $CommonTests -KeyFile 181 | } 182 | 183 | Context 'Validating with incorrect Keyfile' { 184 | BeforeAll { 185 | $KeyFileName = 'TestdbKeyFileAndMasterPassword.key' 186 | 187 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 188 | $KeePassDatabaseSuffix = 'KeyFile' 189 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 190 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 191 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 192 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 193 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 194 | 195 | $vaultParams = @{ 196 | VaultName = $VaultName 197 | VaultParameters = @{ 198 | Path = $vaultPath 199 | KeyPath = $KeyPath 200 | } 201 | } 202 | } 203 | . $CommonTests -Invalid -KeyFile 204 | } 205 | 206 | Context 'Validating with correct Keyfile v2' { 207 | BeforeAll { 208 | $KeyFileName = 'TestdbKeyFileV2.keyx' 209 | $KeePassDatabaseSuffix = 'KeyFileV2' 210 | 211 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 212 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 213 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 214 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 215 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 216 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 217 | 218 | $vaultParams = @{ 219 | VaultName = $VaultName 220 | VaultParameters = @{ 221 | Path = $vaultPath 222 | KeyPath = $KeyPath 223 | } 224 | } 225 | } 226 | . $CommonTests -KeyFile 227 | } 228 | 229 | Context 'Validating with correct Keyfile and correct master password' { 230 | BeforeAll { 231 | $KeyFileName = 'TestdbKeyFileAndMasterPassword.key' 232 | $MasterKey = '"1}`.2R{LX1`Jm8%XX2/' 233 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 234 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 235 | $KeePassDatabaseSuffix = 'KeyFileAndMasterPassword' 236 | 237 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 238 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 239 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 240 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 241 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 242 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 243 | 244 | $vaultParams = @{ 245 | VaultName = $VaultName 246 | VaultParameters = @{ 247 | Path = $VaultPath 248 | UseMasterPassword = $true 249 | KeyPath = $KeyPath 250 | } 251 | } 252 | } 253 | . $CommonTests -Credential 254 | } 255 | 256 | Context 'Validating with correct Keyfile and incorrect master password' { 257 | BeforeAll { 258 | $KeyFileName = 'TestdbKeyFileAndMasterPassword.key' 259 | $MasterKey = 'NotTheCorrectMasterKey' 260 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 261 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 262 | $KeePassDatabaseSuffix = 'KeyFileAndMasterPassword' 263 | 264 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 265 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 266 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 267 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 268 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 269 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 270 | 271 | $vaultParams = @{ 272 | VaultName = $VaultName 273 | VaultParameters = @{ 274 | Path = $VaultPath 275 | UseMasterPassword = $true 276 | KeyPath = $KeyPath 277 | } 278 | } 279 | } 280 | . $CommonTests -Credential -Invalid 281 | } 282 | 283 | 284 | Context 'Validating with incorrect Keyfile and correct master password' { 285 | BeforeAll { 286 | $KeyFileName = 'TestdbKeyFile.key' 287 | $MasterKey = '"1}`.2R{LX1`Jm8%XX2/' 288 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 289 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 290 | $KeePassDatabaseSuffix = 'KeyFileAndMasterPassword' 291 | 292 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 293 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 294 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 295 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 296 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 297 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 298 | 299 | $vaultParams = @{ 300 | VaultName = $VaultName 301 | VaultParameters = @{ 302 | Path = $VaultPath 303 | UseMasterPassword = $true 304 | KeyPath = $KeyPath 305 | } 306 | } 307 | } 308 | . $CommonTests -Credential -Invalid 309 | } 310 | 311 | Context 'Validating with incorrect Keyfile and incorrect master password' { 312 | BeforeAll { 313 | $KeyFileName = 'TestdbKeyFile.key' 314 | $MasterKey = 'You can not enter with this password!' 315 | $VaultMasterKey = [PSCredential]::new('vaultkey', (ConvertTo-SecureString -AsPlainText -Force $MasterKey)) 316 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 317 | $KeePassDatabaseSuffix = 'KeyFileAndMasterPassword' 318 | 319 | $VaultName = "KeepassPesterTest_$([guid]::NewGuid())" 320 | $KeePassDatabaseFileName = "$($BaseKeepassDatabaseName)$($KeePassDatabaseSuffix).kdbx" 321 | $VaultPath = Join-Path -Path $TestDrive -ChildPath $KeePassDatabaseFileName 322 | $KeyPath = Join-Path -Path $TestDrive -ChildPath $KeyFileName 323 | Copy-Item -Path (Join-Path $Mocks $KeePassDatabaseFileName) -Destination $VaultPath 324 | Copy-Item -Path (Join-Path $Mocks $KeyFileName) -Destination $KeyPath 325 | 326 | $vaultParams = @{ 327 | VaultName = $VaultName 328 | VaultParameters = @{ 329 | Path = $VaultPath 330 | UseMasterPassword = $true 331 | KeyPath = $KeyPath 332 | } 333 | } 334 | } 335 | . $CommonTests -Credential -Invalid 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /SecretManagement.KeePass/Tests/TestSecretVault-CommonTests.include.ps1: -------------------------------------------------------------------------------- 1 | param( 2 | [Switch]$Credential, 3 | [Switch]$KeyFile, 4 | [Switch]$Invalid 5 | ) 6 | 7 | It "should not have a vault variable by default" { 8 | { 9 | InModuleScope $ExtensionModule { 10 | param($vaultName) 11 | Get-Variable "Vault_$vaultName" -ErrorAction 'Stop' 12 | } @{ 13 | vaultName = $vaultParams.VaultName 14 | } 15 | } | Should -Throw 'Cannot find a variable with the name*' 16 | } 17 | 18 | if (-not $Invalid) { 19 | if ($KeyFile) { 20 | It 'Should not request a credential' { 21 | Set-ItResult -Skipped -Because 'Broken by SecretManagement 1.1.0 new runspace behavior' 22 | Test-SecretVault @vaultParams 23 | Should -Invoke -CommandName 'Get-Credential' -Exactly 0 -Scope Context 24 | } 25 | } 26 | 27 | if ($Credential) { 28 | It 'should request a credential on the first pass' { 29 | Mock -Verifiable -ModuleName $ExtModuleName -CommandName 'Get-Credential' -MockWith { $VaultMasterKey } 30 | Test-SecretVault @vaultParams 31 | Should -ModuleName $ExtModuleName -Invoke -CommandName 'Get-Credential' -Exactly 1 -Scope Context 32 | } 33 | It 'Should not request a credential on the second pass' { 34 | Test-SecretVault @vaultParams 35 | Test-SecretVault @vaultParams 36 | Should -ModuleName $ExtModuleName -Invoke -CommandName 'Get-Credential' -Exactly 1 -Scope Context 37 | } 38 | } 39 | 40 | It 'should have a Vault variable upon unlock' { 41 | Test-SecretVault @vaultParams | Should -BeTrue 42 | $vaultVars = InModuleScope $ExtensionModule { 43 | (Get-Variable -Name Vault_*).Name 44 | } 45 | "Vault_$($vaultParams.VaultName)" | Should -BeIn $vaultVars 46 | } 47 | 48 | It 'should return true' { 49 | Test-SecretVault @vaultParams | Should -BeTrue 50 | } 51 | 52 | } else { 53 | It 'Detects Invalid Composite Key and does not set a vault variable' { 54 | $infoString = Get-Module microsoft.powershell.secretmanagement | Format-Table | Out-String 55 | Write-PSFMessage -Level Verbose -Message "$infoString" 56 | $result = Test-SecretVault @vaultParams -ErrorVariable myerr 2>$null 57 | $myerr[-2] | Should -BeLike $KeePassMasterKeyError 58 | $result | Should -BeFalse 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | #requires -version 5 2 | using namespace System.IO 3 | 4 | <# 5 | .SYNOPSIS 6 | Bootstraps Invoke-Build and starts it with supplied parameters. 7 | .NOTES 8 | If you already have Invoke-Build installed, just use Invoke-Build instead of this script. This is for CI/CD environments like Appveyor, Jenkins, or Azure DevOps pipelines. 9 | .EXAMPLE 10 | .\build.ps1 11 | Starts Invoke-Build with the default parameters 12 | #> 13 | 14 | $ErrorActionPreference = 'Stop' 15 | 16 | #Add TLS 1.2 to potential security protocols on Windows Powershell. This is now required for powershell gallery 17 | if ($PSEdition -eq 'Desktop') { 18 | [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor 'Tls12' 19 | } 20 | 21 | function BootstrapModule { 22 | param ( 23 | $ModuleSpecification, 24 | $Path = (Join-Path ([Environment]::GetFolderPath('LocalApplicationData')) 'Press') 25 | ) 26 | $vEnvDir = New-Item -ItemType Directory -Force -Path $Path 27 | 28 | $env:PSModulePath = $vEnvDir,$env:PSModulePath -join [io.path]::PathSeparator 29 | 30 | #This is done for performance. If the module is found loaded it won't try to search filesystem 31 | $existingModule = (Get-Module -FullyQualifiedName $moduleSpecification -ErrorAction SilentlyContinue) 32 | if (-not $existingModule) { 33 | $existingModule = (Get-Module -ListAvailable -FullyQualifiedName $moduleSpecification -ErrorAction SilentlyContinue) 34 | } 35 | 36 | if ($existingModule) { 37 | Write-Verbose "Module $($moduleSpecification.ModuleName) was detected. Skipping bootstrap." 38 | return 39 | } 40 | 41 | $moduleParams = @{ 42 | Name = $moduleSpecification.ModuleName 43 | MinimumVersion = $moduleSpecification.ModuleVersion 44 | MaximumVersion = $moduleSpecification.MaximumVersion 45 | Force = $true 46 | ErrorAction = 'Stop' 47 | } 48 | Write-Verbose "$($ModuleSpecification.ModuleName) not found locally. Bootstrapping..." 49 | Save-Module @moduleParams -Path $vEnvDir 50 | Import-Module @moduleParams 51 | } 52 | 53 | BootstrapModule @{ 54 | ModuleName = 'InvokeBuild' 55 | ModuleVersion = '5.5.7' 56 | MaximumVersion = '5.99.99' 57 | } 58 | 59 | #Passthrough Invoke-Build 60 | Push-Location $PSScriptRoot 61 | try { 62 | Invoke-Expression "Invoke-Build $($args -join ' ')" 63 | } catch { 64 | throw $PSItem 65 | } finally { 66 | Pop-Location 67 | } -------------------------------------------------------------------------------- /images/KeePassSecretManagementDemo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/images/KeePassSecretManagementDemo.gif -------------------------------------------------------------------------------- /images/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JustinGrote/SecretManagement.KeePass/cb0ddb3355b3e14c672679d687bbd8e5f5979bdf/images/Logo.png --------------------------------------------------------------------------------