PowerShell 6 Core Support #35

Merged
OCram85 merged 10 commits from dev into master 2019-01-16 12:55:30 +01:00
75 changed files with 2405 additions and 1322 deletions
Showing only changes of commit 521e1f1091 - Show all commits

2
.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
src/Vendor/libressl255/* filter=lfs diff=lfs merge=lfs -text
*.pfx filter=lfs diff=lfs merge=lfs -text

43
.travis.yml Normal file
View File

@ -0,0 +1,43 @@
language: csharp
dotnet: 2.2.101
mono: none
git:
depth: 1000
os:
- linux
# Disable OSX bulds for now
# - osx
sudo: required
dist: xenial
osx_image: xcode8.1
matrix:
fast_finish: true
addons:
artifacts:
#paths: $(ls ./../dist/PowerShellGet.zip | tr "\n" ":")
paths: ./dist/PowerShellGet.zip
install:
# Default 2.0.0 Ruby is buggy
# Default bundler version is buggy
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
rvm install ruby-2.3.3;
rvm --default use 2.3.3;
fi
- bash <(wget -O - https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.sh)
- pushd tools
- chmod +x travis.sh
- popd
script:
- echo "TRAVIS_EVENT_TYPE value $TRAVIS_EVENT_TYPE"
- ./tools/travis.sh

2
.vscode/launch.json vendored
View File

@ -42,7 +42,7 @@
"type": "PowerShell", "type": "PowerShell",
"request": "launch", "request": "launch",
"name": "PowerShell Interactive Session", "name": "PowerShell Interactive Session",
"cwd": "${workspaceRoot}" "cwd": ""
} }
] ]
} }

View File

@ -26,7 +26,8 @@
"powershell.codeFormatting.whitespaceAroundOperator": true, "powershell.codeFormatting.whitespaceAroundOperator": true,
"powershell.codeFormatting.whitespaceAfterSeparator": true, "powershell.codeFormatting.whitespaceAfterSeparator": true,
"powershell.codeFormatting.ignoreOneLineBlock": true, "powershell.codeFormatting.ignoreOneLineBlock": true,
"powershell.codeFormatting.alignPropertyValuePairs": false, "powershell.codeFormatting.alignPropertyValuePairs": true,
"powershell.codeFormatting.preset": "Custom",
// cspell spellchecker options // cspell spellchecker options
"cSpell.enabledLanguageIds": [ "cSpell.enabledLanguageIds": [
"c", "c",

View File

@ -20,6 +20,7 @@ image: Visual Studio 2017
install: install:
- ps: Import-Module .\tools\AppVeyor.psm1 - ps: Import-Module .\tools\AppVeyor.psm1
- ps: Import-Module .\tools\CoverallsIO.psm1
- ps: Invoke-InstallDependencies - ps: Invoke-InstallDependencies
environment: environment:
@ -37,10 +38,10 @@ build_script:
- ps: Invoke-AppVeyorBuild - ps: Invoke-AppVeyorBuild
test_script: test_script:
- ps: Invoke-AppVeyorTests
- ps: | - ps: |
$CodeCoverage = Invoke-AppVeyorTests
if ($null -ne $Env:CoverallsToken) { if ($null -ne $Env:CoverallsToken) {
Invoke-CoverageReport Invoke-CoverageReport -PesterCoverageReport $CodeCoverage
} }
else { else {
Write-Warning "No CoverallsToken found. This build seems to be triggered by a PR. Skipping this step..." Write-Warning "No CoverallsToken found. This build seems to be triggered by a PR. Skipping this step..."
@ -60,7 +61,7 @@ deploy:
secure: M+bBX5/nKdJB0eViP7xtrLVTwf3vGDUA9N2MMprZp2i+9ZR3CBVcJnSzJWUmalhB secure: M+bBX5/nKdJB0eViP7xtrLVTwf3vGDUA9N2MMprZp2i+9ZR3CBVcJnSzJWUmalhB
artifact: PSCredentialStore.zip # upload all NuGet packages to release assets artifact: PSCredentialStore.zip # upload all NuGet packages to release assets
draft: false draft: false
prerelease: false prerelease: true
on: on:
branch: master # build release on master branch changes branch: master # build release on master branch changes

BIN
assets/logo256.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

@ -1,3 +1,3 @@
{ {
"Version": "1.2.0", "Version": "2.0.0",
"Creation": "2016-06-14 08:41:10" "Creation": "2016-06-14 08:41:10"

View File

@ -1 +0,0 @@
!マ<><EFBE8F>H4サ<34><EFBDBB>"=w肛Sヨ2

Binary file not shown.

BIN
resources/cs/PSCredentialStore.pfx (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,100 @@
function New-CRTAttribute {
<#
.SYNOPSIS
Create required data for a certificate signing request.
.DESCRIPTION
Defines the certificate related properties for an upcoming New-PfxCertificate execution.
.PARAMETER Country
Provide a two letter country code.
.PARAMETER State
Certificate state value.
.PARAMETER City
Certificate city value.
.PARAMETER Organization
Certificate organization value.
.PARAMETER OrganizationalUnitName
Certificate OrganizationalUnitName value.
.PARAMETER CommonName
The certificate common name.
.PARAMETER CSRSubject
you can provide the needed certificate properties with in one hashtable. This hashtable has to contain the
following keys: 'Country', 'State', 'City', 'Organization', 'OrganizationalUnitName', 'CommonName'.
.INPUTS
[None]
.OUTPUTS
['PSCredentialStore.Certificate.CSRDetails']
.EXAMPLE
New-CRTAttribute -CSRSubject @{Country = 'DE'; State = 'BW'; City = 'Karlsruhe'; Organization = 'AwesomeIT'; OrganizationalUnitName = '';CommonName = 'MyPrivateCert'}
.NOTES
File Name : New-CSRDetails.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
[OutputType('PSCredentialStore.Certificate.Attribute')]
param(
[Parameter(Mandatory = $true)]
[ValidateLength(2, 2)]
[ValidateNotNull()]
[string]$Country,
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[string]$State,
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[string]$City,
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[string]$Organization,
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[string]$OrganizationalUnitName,
[Parameter(Mandatory = $true)]
[ValidateNotNull()]
[string]$CommonName,
[Parameter(Mandatory = $false)]
[ValidateNotNull()]
[int]$Days = 365
)
begin {
}
process {
return [PSCustomObject]@{
PSTypeName = 'PSCredentialStore.Certificate.Attribute'
Subject = [PSCustomObject]@{
PSTypeName = 'PSCredentialStore.Certificate.Attribute.Subject'
Country = $Country
State = $State
City = $City
Organization = $Organization
OrganizationalUnitName = $OrganizationalUnitName
CommonName = $CommonName
}
Days = $Days
}
}
end {
}
}

View File

@ -0,0 +1,142 @@
function New-PfxCertificate {
<#
.SYNOPSIS
Creates new PFX certificate for the CredentialStore encryption.
.DESCRIPTION
Use this function to create a custom self signed certificate used by the PSCredentialStore module.
.PARAMETER CRTAttribute
Provide certificate related attributes provided by function New-CRTAttribute.
.PARAMETER KeyName
Provide a custom full path and name for the private key. The file extension has to be `*.key`.
.PARAMETER CertName
Provide a custom full path and name for the PFX certificate file. The file extension has to be `*.pfx`
.INPUTS
[PSCredentialStore.Certificate.Attribute]
.OUTPUTS
[None]
.EXAMPLE
New-PfxCertificate -CRTAttribute $CRTAttribute -KeyName './myprivate.key' -CertName './mycert.pfx'
.NOTES
File Name : New-PfxCertificate.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding(SupportsShouldProcess = $true)]
[OutputType()]
param(
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[ValidateNotNullOrEmpty()]
[PSTypeName('PSCredentialStore.Certificate.Attribute')]$CRTAttribute,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$KeyName = './private.key',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$CertName = './certificate.pfx'
)
begin {
$ModuleBase = Get-ModuleBase
if ($isLinux -or $isMacOS) {
try {
$openssl = Get-Command -Name 'openssl' -ErrorAction Stop
}
catch {
$_.Exception.Message | Write-Error
$ErrorParams = @{
Message = 'Can not find the openssl binary!'
ErrorAction = 'Stop'
Exception = [System.IO.FileNotFoundException]::new()
}
Write-Error @ErrorParams
}
}
elseif (($PSVersionTable.PSEdition -eq 'Desktop' -and $PSVersionTable.PSVersion.Major -lt 6) -or ($IsWindows -eq $true)) {
$openssl = Join-Path -Path $ModuleBase -ChildPath '/Vendor/libressl255/openssl.exe'
}
$Env:OPENSSL_CONF = Join-Path $ModuleBase -ChildPath '/openssl.conf'
}
process {
$SubjPattern = "/C={0}/ST={1}/L={2}/O={3}/OU={4}/CN={5}"
$SubjValues = @(
$CRTAttribute.Subject.Country,
$CRTAttribute.Subject.State,
$CRTAttribute.Subject.City,
$CRTAttribute.Subject.Organization,
$CRTAttribute.Subject.OrganizationalUnitName,
$CRTAttribute.Subject.CommonName
)
$Subj = $SubjPattern -f $SubjValues
$PEMCertName = $CertName -replace '.pfx', '.crt'
$ExpPattern = '& ''{0}'' req -x509 -sha256 -nodes -days {1} -newkey rsa:2048 -keyout {2} -out {3} -subj "{4}" *>$null'
$ExpValues = @(
$openssl,
$CRTAttribute.Days
$KeyName,
$PEMCertName,
$Subj
)
$PEMExp = $ExpPattern -f $ExpValues
Write-Verbose -Message ( 'Expr string is: {0}' -f $PEMExp)
# Edit the Error action for the openSLL command to make the redirect *>$null work.
# There is always a stderr and stdout stream!
$EAP = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
Invoke-Expression -Command $PEMExp
$ErrorActionPreference = $EAP
# manually testing the openssl command results
if (! (Test-Path -Path $KeyName)) {
$ErrorParams = @{
Message = 'Could not create the private key ${0}' -f $KeyName
ErrorAction = 'Stop'
Exception = [System.UnauthorizedAccessException]::new()
}
Write-Error @ErrorParams
}
if (! (Test-Path -Path $PEMCertName)) {
$ErrorParams = @{
Message = 'Could not create the PEM certificate ${0}' -f $PEMCertName
ErrorAction = 'Stop'
Exception = [System.Exception]::new()
}
Write-Error @ErrorParams
}
$PfxPattern = '& ''{0}'' pkcs12 -export -out {1} -inkey {2} -in {3} -passout pass:'
$PfxValues = @(
$openssl,
$CertName,
$KeyName,
($CertName -replace '.pfx', '.crt')
)
$PfxExp = $PfxPattern -f $PfxValues
Write-Verbose -Message ( 'PfxExp string is: {0}' -f $PfxExp)
Invoke-Expression -Command $PfxExp
# Remove private key and crt file. Always ask user
Remove-Item -Path $KeyName
Remove-Item -Path ($CertName -replace '.pfx', '.crt')
}
end {
Remove-Item Env:\OPENSSL_CONF -Confirm:$False -Force -ErrorAction SilentlyContinue
}
}

View File

@ -0,0 +1,102 @@
function Use-PfxCertificate {
<#
.SYNOPSIS
Links an existing PFX Certifiacte to a CredentialStore.
.DESCRIPTION
Linking a certificate is needed if you plan to use the same CredentialStore in cross platform scenarios.
.PARAMETER Path
Specify the path to the PFX Certificate you want to link for usage.
.INPUTS
[None]
.OUTPUTS
[None]
.EXAMPLE
.NOTES
File Name : Use-PfxCertificate.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding(DefaultParameterSetName = "Private")]
[OutputType()]
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSAvoidUsingPlainTextForPassword", "")]
param(
[Parameter(Mandatory = $true, ParameterSetName = "Private")]
[Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[ValidateNotNullOrEmpty()]
[string]$CredentialStore,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared
)
begin {}
process {
try {
# We need to resolve the path to make sure it has the correct platform specific syntax.
# And it should also exist.
$validPath = Resolve-Path -Path $Path -ErrorAction Stop
$PfxCertificate = Get-PfxCertificate -FilePath $validPath -ErrorAction Stop
}
catch {
$_.Exception.Error | Write-Error
$ErrorParams = @{
Message = 'The given PFX certificate does not exist!'
ErrorAction = 'Stop'
}
Write-Error @ErrorParams
}
try {
if ($PSCmdlet.ParameterSetName -eq "Private") {
$StorePath = Get-DefaultCredentialStorePath
$CS = Get-CredentialStore
}
elseif ($PSCmdlet.ParameterSetName -eq "Shared" ) {
if (!($PSBoundParameters.ContainsKey('CredentialStore'))) {
$StorePath = Get-DefaultCredentialStorePath -Shared
$CS = Get-CredentialStore -Shared
}
else {
$StorePath = $CredentialStore
$CS = Get-CredentialStore -Shared -Path $CredentialStore
}
}
}
catch {
$_.Exception.Error | Write-Error
$ErrorParams = @{
Message = 'The given CredentialStore does not exist!'
ErrorAction = 'Stop'
}
Write-Error @ErrorParams
}
# Lets first check if the thumbprint matches
if (($CS.Thumbprint -notmatch $PfxCertificate.Thumbprint) -and ($CS.Thumbprint.Length -ne 0)) {
Write-Warning @"
You are trying to map an unknown certificate.
Make sure you used the same AES keys for encrypting!
"@
}
$CS.PfxCertificate = $validPath.Path
$CS.Thumbprint = $PfxCertificate.Thumbprint
$CS | ConvertTo-Json -Depth 5 | Out-File -FilePath $StorePath -Force -Encoding utf8
}
end {}
}

View File

@ -1,45 +0,0 @@
function Get-ChallengeFile {
<#
.SYNOPSIS
Reads the challenge file as binary content.
.DESCRIPTION
Use this function to tread a challenge file. Returns a [Byte[]] Array.
.PARAMETER Path
Specify a file to read.
.INPUTS
[None]
.OUTPUTS
[Byte[]]
.EXAMPLE
.\Get-RandomKey -Path "C:\TMP\Challenge.bin"
.NOTES
```
File Name : Get-ChallengeFile.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
```
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$Path = "{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData
)
if (Test-Path $Path) {
try {
[io.file]::ReadAllBytes($Path)
}
catch {
Write-Error ("Could not read file {0}." -f $Path) -ErrorAction Stop
}
}
}

View File

@ -1,72 +0,0 @@
Function Set-ChallengeFile() {
<#
.SYNOPSIS
Writes the given key into the challenge file
.DESCRIPTION
You can use the file content for ConvertTo-SecureString operations.
.PARAMETER Path
The file you wish to create.
.PARAMETER KeySize
Specify the key size for the encryption key.
.PARAMETER Force
Use this switch to override an older file version.
.INPUTS
[None]
.OUTPUTS
[None]
.EXAMPLE
.\Set-ChallengeFile -Path "C:\TMP\myfile.json" -Force
.NOTES
File Name : Set-ChallengeFile.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[string]$Path = "{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData,
[Parameter(Mandatory = $false)]
[ValidateSet(16, 24, 32)]
[string]$KeySize = "24",
[switch]$Force
)
if ((Test-Path -Path $Path)) {
if ($Force -eq $true) {
Remove-Item -Path $Path -Confirm:$false -Force
}
else {
Write-Error "The given file already exists!. Use the -Force switch to override it." -ErrorAction Stop
}
}
$PSCredentialStoreDataDir = Split-Path -Path $Path -Parent
if (-not (Test-Path $PSCredentialStoreDataDir)) {
try {
New-Item -ItemType Directory -Path $PSCredentialStoreDataDir
}
catch {
Write-Error ("Could not create the parent data dir {0}" -f $PSCredentialDataDir) -ErrorAction Stop
}
}
try {
$Keys = Get-RandomKey -Size $KeySize
[io.file]::WriteAllBytes($Path, $Keys)
}
catch {
$_.Exception | Format-List -Force | Out-String | Write-Error -ErrorAction Stop
}
}

View File

@ -1,48 +0,0 @@
function Test-ChallengeFile {
<#
.SYNOPSIS
Simple path check for challenge file needed by the CredentialStores.
.DESCRIPTION
This is supposed to be a internal function to check the existence for a challenge file.
.PARAMETER Path
Specify the path to the challenge file.
.INPUTS
[None]
.OUTPUTS
[Bool].
.EXAMPLE
If (Test-ChallengeFile) {
Write-Host "The file exists."
}
Else {
Write-Warning "Couldn't find the given file!"
}
.NOTES
File Name : Test-ChallengeFile.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[String]$Path = "{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData
)
if (Test-Path $Path) {
$true
}
else {
$false
}
}

View File

@ -93,15 +93,30 @@ function Connect-To {
[Parameter(Mandatory = $False, ParameterSetName = "Private")] [Parameter(Mandatory = $False, ParameterSetName = "Private")]
[PSCredential]$Credentials, [PSCredential]$Credentials,
[Parameter(Mandatory = $true, ParameterSetNAme = "Shared")]
[switch]$Shared,
[Parameter(Mandatory = $False, ParameterSetName = "Shared")] [Parameter(Mandatory = $False, ParameterSetName = "Shared")]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData, [string]$Path,
[Parameter(Mandatory = $false, ParameterSetNAme = "Shared")] [Parameter(Mandatory = $False, ParameterSetName = "Private")]
[switch]$Shared [Parameter(Mandatory = $False, ParameterSetName = "Shared")]
[switch]$PassThru
) )
begin { begin {
# Set the CredentialStore for private, shared or custom mode.
Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = Get-DefaultCredentialStorePath
}
elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
}
}
# First check the optional modules # First check the optional modules
if (-not (Resolve-Dependency -Name $Type)) { if (-not (Resolve-Dependency -Name $Type)) {
Write-Error -Message ("Could not resolve the optional dependencies defined for {0}" -f $Type) -ErrorAction 'Stop' Write-Error -Message ("Could not resolve the optional dependencies defined for {0}" -f $Type) -ErrorAction 'Stop'
@ -118,10 +133,6 @@ function Connect-To {
} }
process { process {
# Set the correct CredentialStore Path depending on the used ParameterSetName
if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = "{0}\CredentialStore.json" -f $env:APPDATA
}
if (-not ($Credentials)) { if (-not ($Credentials)) {
# Load the credential from the CredentialStore. If the credential doesn't exist, we need to # Load the credential from the CredentialStore. If the credential doesn't exist, we need to
# return 1, so a calling if statement can handle the failure detection. # return 1, so a calling if statement can handle the failure detection.
@ -131,16 +142,16 @@ function Connect-To {
try { try {
if ($Identifier -ne "") { if ($Identifier -ne "") {
$RemoteHostIdentifier = "{0}/{1}" -f $Identifier, $RemoteHost $RemoteHostIdentifier = "{0}/{1}" -f $Identifier, $RemoteHost
$creds = Get-CredentialStoreItem -RemoteHost $RemoteHostIdentifier -Path $Path $creds = Get-CredentialStoreItem -Shared -RemoteHost $RemoteHostIdentifier -Path $Path
} }
else { else {
$creds = Get-CredentialStoreItem -RemoteHost $RemoteHost -Path $Path $creds = Get-CredentialStoreItem -Shared -RemoteHost $RemoteHost -Path $Path
} }
} }
catch { catch {
$MessageParams = @{ $MessageParams = @{
Message = "Unable to look up credential store item for RemoteHost {0}/Identifier {1}!" -f $RemoteHost, $Identifier Message = "Unable to look up credential store item for RemoteHost {0}/Identifier {1}!" -f $RemoteHost, $Identifier
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -152,7 +163,7 @@ function Connect-To {
if ($creds.UserName -eq "" -or $creds.Password.GetType().Name -ne "SecureString") { if ($creds.UserName -eq "" -or $creds.Password.GetType().Name -ne "SecureString") {
$MessageParams = @{ $MessageParams = @{
Message = "Please provide valid credentials for RemoteHost {0}!" -f $RemoteHost Message = "Please provide valid credentials for RemoteHost {0}!" -f $RemoteHost
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -167,7 +178,7 @@ function Connect-To {
catch { catch {
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -177,9 +188,9 @@ function Connect-To {
# First establish the FTP session # First establish the FTP session
$WinSCPConParams = @{ $WinSCPConParams = @{
Credential = $creds Credential = $creds
Hostname = $RemoteHost Hostname = $RemoteHost
Protocol = 'Ftp' Protocol = 'Ftp'
FtpMode = 'Passive' FtpMode = 'Passive'
} }
try { try {
$FTPSessionOption = New-WinSCPSessionOption @WinSCPConParams $FTPSessionOption = New-WinSCPSessionOption @WinSCPConParams
@ -192,7 +203,7 @@ function Connect-To {
if (!($WinSCPSession.Opened)) { if (!($WinSCPSession.Opened)) {
# Check the connection state and find out if the session is still open. # Check the connection state and find out if the session is still open.
$MessageParams = @{ $MessageParams = @{
Message = "Connection to {0} using Type {1} was established. But now it seems to be lost!" -f $RemoteHost, $Type Message = "Connection to {0} using Type {1} was established. But now it seems to be lost!" -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -206,7 +217,7 @@ function Connect-To {
catch { catch {
# Write a error message to the log. # Write a error message to the log.
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -220,7 +231,7 @@ function Connect-To {
catch { catch {
# Write a error message to the log. # Write a error message to the log.
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -228,13 +239,19 @@ function Connect-To {
} }
"CisServer" { "CisServer" {
try { try {
Connect-CisServer -Server $RemoteHost -Credential $creds -ErrorAction Stop | Out-Null if ($PassThru.IsPresent) {
Connect-CisServer -Server $RemoteHost -Credential $creds -ErrorAction Stop
}
else {
Connect-CisServer -Server $RemoteHost -Credential $creds -ErrorAction Stop | Out-Null
}
} }
catch { catch {
# Write a error message to the log. # Write a error message to the log.
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -243,17 +260,17 @@ function Connect-To {
"ExchangeHTTP" { "ExchangeHTTP" {
try { try {
$ConnectionParams = @{ $ConnectionParams = @{
ConnectionURI = "http://{0}/powershell" -f $RemoteHost ConnectionURI = "http://{0}/powershell" -f $RemoteHost
ConfigurationName = 'Microsoft.Exchange' ConfigurationName = 'Microsoft.Exchange'
Credential = $creds Credential = $creds
ErrorAction = 'Stop' ErrorAction = 'Stop'
} }
$Global:PSExchangeRemote = New-PSSession @ConnectionParams $Global:PSExchangeRemote = New-PSSession @ConnectionParams
} }
catch { catch {
# Write a error message to the log. # Write a error message to the log.
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -262,17 +279,17 @@ function Connect-To {
"ExchangeHTTPS" { "ExchangeHTTPS" {
try { try {
$ConnectionParams = @{ $ConnectionParams = @{
ConnectionURI = "https://{0}/powershell" -f $RemoteHost ConnectionURI = "https://{0}/powershell" -f $RemoteHost
ConfigurationName = 'Microsoft.Exchange' ConfigurationName = 'Microsoft.Exchange'
Credential = $creds Credential = $creds
ErrorAction = 'Stop' ErrorAction = 'Stop'
} }
$Global:PSExchangeRemote = New-PSSession @ConnectionParams $Global:PSExchangeRemote = New-PSSession @ConnectionParams
} }
catch { catch {
# Write a error message to the log. # Write a error message to the log.
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -280,9 +297,9 @@ function Connect-To {
} }
"SCP" { "SCP" {
$WinSCPSessionParams = @{ $WinSCPSessionParams = @{
Credential = $creds Credential = $creds
Hostname = $RemoteHost Hostname = $RemoteHost
Protocol = 'Scp' Protocol = 'Scp'
GiveUpSecurityAndAcceptAnySshHostKey = $True GiveUpSecurityAndAcceptAnySshHostKey = $True
} }
try { try {
@ -293,7 +310,7 @@ function Connect-To {
catch { catch {
# Write a error message to the log. # Write a error message to the log.
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -302,7 +319,7 @@ function Connect-To {
if (!($WinSCPSession.Opened)) { if (!($WinSCPSession.Opened)) {
# Check the connection state and find out if the session is still open. # Check the connection state and find out if the session is still open.
$MessageParams = @{ $MessageParams = @{
Message = "Connection to {0} using Type {1} was established. But now it seems to be lost!" -f $RemoteHost, $Type Message = "Connection to {0} using Type {1} was established. But now it seems to be lost!" -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
@ -311,7 +328,7 @@ function Connect-To {
default { default {
# Write a error message to the log. # Write a error message to the log.
$MessageParams = @{ $MessageParams = @{
Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type Message = "Unable to connect to {0} using Type {1}." -f $RemoteHost, $Type
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams

View File

@ -1,50 +0,0 @@
function Get-RandomKey {
<#
.SYNOPSIS
Returns a random key
.DESCRIPTION
You can use the key for further use with SecureStrings.
.PARAMETER Size
Define the key size. You can choose between 16, 24 and 32
.INPUTS
[None]
.OUTPUTS
Returns a Random key as [Byte[]] array.
.EXAMPLE
.\Get-RandomKey -Size 24
.NOTES
```
File Name : Get-RandomKey.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
```
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[ValidateSet(16, 24, 32)]
[string]$size
)
# Init the vars
[Byte[]]$Key = @()
$i = 0
while ($i -ne $size) {
$element = Get-Random -Minimum 0 -Maximum 255
Write-Debug ("The current element is {0}." -f $element)
$Key += $element
$i++
}
$Key
}

View File

@ -41,11 +41,8 @@ function Get-CredentialStoreItem {
#> #>
[CmdletBinding(DefaultParameterSetName = "Private")] [CmdletBinding(DefaultParameterSetName = "Private")]
[OutputType([System.Management.Automation.PSCredential])] [OutputType([PSCredential])]
param( param(
[Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")] [Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[Parameter(Mandatory = $true, ParameterSetName = "Private")] [Parameter(Mandatory = $true, ParameterSetName = "Private")]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
@ -56,55 +53,72 @@ function Get-CredentialStoreItem {
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string]$Identifier, [string]$Identifier,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[switch]$Shared [ValidateNotNullOrEmpty()]
[string]$Path
) )
# First set a constand path for private CredentialStore mode. begin {
if ($PSCmdlet.ParameterSetName -eq "Private") { # Set the CredentialStore for private, shared or custom mode.
$Path = "{0}\CredentialStore.json" -f $env:APPDATA Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = Get-DefaultCredentialStorePath
}
elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
}
}
} }
if ($Identifier -ne "") { process {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost if ($Identifier -ne "") {
} $CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
else { }
$CredentialName = $RemoteHost else {
} $CredentialName = $RemoteHost
}
if (Test-CredentialStore -Path $Path) { if (Test-CredentialStore -Shared -Path $Path) {
$CS = Get-CredentialStore -Path $Path $CS = Get-CredentialStore -Shared -Path $Path
$CSMembers = Get-Member -InputObject $CS $CSMembers = Get-Member -InputObject $CS
# Let`s first check if the given remote host exists as object property # Let's first check if the given remote host exists as object property
if (($CSMembers.MemberType -eq "NoteProperty") -and ($CSMembers.Name -eq $CredentialName)) { if (($CSMembers.MemberType -eq "NoteProperty") -and ($CSMembers.Name -contains $CredentialName)) {
if ($CS.Type -eq "Private") { $Cert = Get-PfxCertificate -FilePath $CS.PfXCertificate -ErrorAction Stop
$CSItem = [ordered]@{ $DecryptedKey = $Cert.PrivateKey.Decrypt(
User = $CS.$CredentialName.User [Convert]::FromBase64String($CS.$CredentialName.EncryptedKey),
Password = ConvertTo-SecureString -String $CS.$CredentialName.Password [System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1
)
if (! $ExpandOutput.isPresent) {
[PSCredential]::new(
$CS.$CredentialName.User,
($CS.$CredentialName.Password | ConvertTo-SecureString -Key $DecryptedKey)
)
} }
} }
else { else {
$Key = Get-ChallengeFile $MsgParams = @{
$CSItem = [ordered]@{ ErrorAction = "Stop"
User = $CS.$CredentialName.User Message = "Could not find credentials for the given remote host: {0}" -f $RemoteHost
Password = ConvertTo-SecureString -String $CS.$CredentialName.Password -Key $Key
} }
Write-Error @MsgParams
} }
New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $CSItem.User, $CSItem.Password
} }
else { else {
$MsgParams = @{ $MsgParams = @{
ErrorAction = "Stop" ErrorAction = "Stop"
Message = "Could not find credentials for the given remote host: {0}" -f $RemoteHost Message = "The given credential store ({0}) does not exist!" -f $Path
} }
Write-Error @MsgParams Write-Error @MsgParams
} }
} }
else {
$MsgParams = @{ end {
ErrorAction = "Stop"
Message = "The given credential store ({0}) does not exist!" -f $Path
}
Write-Error @MsgParams
} }
} }

View File

@ -42,9 +42,6 @@ function New-CredentialStoreItem {
[CmdletBinding(DefaultParameterSetName = "Private")] [CmdletBinding(DefaultParameterSetName = "Private")]
param( param(
[Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")] [Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[Parameter(Mandatory = $true, ParameterSetName = "Private")] [Parameter(Mandatory = $true, ParameterSetName = "Private")]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
@ -59,78 +56,120 @@ function New-CredentialStoreItem {
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[PSCredential]$Credential, [PSCredential]$Credential,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[switch]$Shared [ValidateNotNullOrEmpty()]
[string]$Path
) )
# First set a constand path for private CredentialStore mode. begin {
if ($PSCmdlet.ParameterSetName -eq "Private") { # Set the CredentialStore for private, shared or custom mode.
$Path = "{0}\CredentialStore.json" -f $env:APPDATA Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
} if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = Get-DefaultCredentialStorePath
# Lets do a quick test on the given CredentialStore.
if (-not(Test-CredentialStore -Path $Path)) {
$MessageParams = @{
Message = "Could not add anything into the given CredentailStore."
ErrorAction = "Stop"
} }
Write-Error @MessageParams elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
} if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
# Read the file content based on the given ParameterSetName }
$CSContent = Get-CredentialStore -Path $Path
$CurrentDate = Get-Date -UFormat "%Y-%m-%d %H:%M:%S"
if ($Identifier -ne "") {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
}
else {
$CredentialName = $RemoteHost
}
if (-not($Credential)) {
$Credential = Get-Credential -Message $CredentialName
}
if ($Credential.UserName) {
if ($CSContent.Type -eq "Shared") {
$Key = Get-ChallengeFile
$encypted = ConvertFrom-SecureString -SecureString $Credential.Password -Key $Key
} }
else { }
$encypted = ConvertFrom-SecureString -SecureString $Credential.Password
} process {
if (Get-Member -InputObject $CSContent -Name $CredentialName -Membertype Properties) { # Lets do a quick test on the given CredentialStore.
if (-not(Test-CredentialStore -Shared -Path $Path)) {
$MessageParams = @{ $MessageParams = @{
Message = "The given host already exists. Nothing to do here." Exception = [System.IO.FileNotFoundException]::new(
'Could not add anything into the given CredentialStore.'
)
ErrorAction = "Stop"
} }
Write-Warning @MessageParams Write-Error @MessageParams
}
# Read the file content based on the given ParameterSetName
<#
if ($PSCmdlet.ParameterSetName -eq 'Private') {
$CSContent = Get-CredentialStore
}
elseif ($PSCmdlet.ParameterSetName -eq 'Shared') {
$CSContent = Get-CredentialStore -Shared -Path $Path
}
#>
$CSContent = Get-CredentialStore -Shared -Path $Path
$CurrentDate = Get-Date -UFormat "%Y-%m-%d %H:%M:%S"
if ($Identifier -ne "") {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
} }
else { else {
$CredentialHash = [ordered]@{ $CredentialName = $RemoteHost
User = $Credential.UserName }
Password = $encypted
Creation = $CurrentDate if (-not($Credential)) {
} $Credential = Get-Credential -Message $CredentialName
Add-Member -InputObject $CSContent -Name $CredentialName -MemberType NoteProperty -Value $CredentialHash }
if ($Credential.UserName) {
try { try {
ConvertTo-Json -InputObject $CSContent | Out-File -FilePath $Path $Cert = Get-PfxCertificate -FilePath $CSContent.PfxCertificate -ErrorAction Stop
} }
catch [System.Exception] { catch {
$MessageParams = @{ $_.Exception.Message | Write-Error
Message = "Couldn't add item into credential store!" $ErrorParams = @{
ErrorAction = "Stop" ErrorAction = 'Stop'
Exception = [System.Security.Cryptography.CryptographicException]::new(
'Could not read the given PFX certificate.'
)
}
Write-Error @ErrorParams
}
if (Get-Member -InputObject $CSContent -Name $CredentialName -Membertype Properties) {
$MessageParams = @{
Message = "The given host already exists. Nothing to do here."
}
Write-Warning @MessageParams
}
else {
$RSAKey = Get-RandomAESKey
$CredentialHash = [ordered]@{
User = $Credential.UserName
Password = ConvertFrom-SecureString -SecureString $Credential.Password -Key $RSAKey
Created = $CurrentDate
LastChange = $null
EncryptedKey = [Convert]::ToBase64String($Cert.PublicKey.Key.Encrypt($RSAKey, [System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1))
}
Add-Member -InputObject $CSContent -Name $CredentialName -MemberType NoteProperty -Value $CredentialHash
try {
ConvertTo-Json -InputObject $CSContent | Out-File -FilePath $Path
}
catch {
$MessageParams = @{
Message = "Couldn't add item into credential store!"
ErrorAction = "Stop"
}
Write-Error @MessageParams
} }
Write-Error @MessageParams
} }
} }
} else {
else { $MessageParams = @{
$MessageParams = @{ Message = "Please Provide at least a valid user!"
Message = "Please Provide at least a valid user!" ErrorAction = "Stop"
ErrorAction = "Stop" }
Write-Error @MessageParams
} }
Write-Error @MessageParams
} }
end {
}
} }

View File

@ -27,8 +27,16 @@ function Remove-CredentialStoreItem {
[None] [None]
.EXAMPLE .EXAMPLE
Remove-CredentialStoreItem -Path "C:\TMP\mystore.json" -RemoteHost "esx01.myside.local" Remove-CredentialStoreItem -RemoteHost "esx01.myside.local"
Remove-CredentialStoreItem -Path "C:\TMP\mystore.json" -RemoteHost "esx01.myside.local" -Identifier svc
.EXAMPLE
Remove-CredentialStoreItem -Shared -RemoteHost "esx01.myside.local"
.EXAMPLE
Remove-CredentialStoreItem -Shared -Path "C:\TMP\mystore.json" -RemoteHost "esx01.myside.local"
.EXAMPLE
Remove-CredentialStoreItem -RemoteHost "esx01.myside.local" -Identifier svc
.NOTES .NOTES
``` ```
@ -43,9 +51,6 @@ function Remove-CredentialStoreItem {
[CmdletBinding(DefaultParameterSetName = "Private")] [CmdletBinding(DefaultParameterSetName = "Private")]
param( param(
[Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData,
[Parameter(Mandatory = $true, ParameterSetName = "Private")] [Parameter(Mandatory = $true, ParameterSetName = "Private")]
[Parameter(Mandatory = $true, ParameterSetName = "Shared")] [Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[string]$RemoteHost, [string]$RemoteHost,
@ -54,43 +59,62 @@ function Remove-CredentialStoreItem {
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[string]$Identifier, [string]$Identifier,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[switch]$Shared [ValidateNotNullOrEmpty()]
[string]$Path
) )
# First set a constand path for private CredentialStore mode. begin {
if ($PSCmdlet.ParameterSetName -eq "Private") { # Set the CredentialStore for private, shared or custom mode.
$Path = "{0}\CredentialStore.json" -f $env:APPDATA Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
} if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = Get-DefaultCredentialStorePath
# Lets do a quick test on the given CredentialStore.
if (-not(Test-CredentialStore -Path $Path)) {
$MessageParams = @{
Message = "Could not add anything into the given CredentailStore."
ErrorAction = "Stop"
} }
Write-Error @MessageParams elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
} if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
# Read the file content based on the given ParameterSetName }
$CSContent = Get-CredentialStore -Path $Path
if ($Identifier -ne "") {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
}
else {
$CredentialName = $RemoteHost
}
if (Get-Member -InputObject $CSContent -Name $CredentialName -Membertype Properties) {
# We need to use the .NET Method because there is no easier way in PowerShell.
$CSContent.PSObject.Properties.Remove($CredentialName)
ConvertTo-Json -InputObject $CSContent | Out-File -FilePath $Path
}
else {
$MessageParams = @{
Message = "The given CredentailStoreItem does not exist."
} }
Write-Warning @MessageParams
} }
process {
# Lets do a quick test on the given CredentialStore.
if (-not(Test-CredentialStore -Shared -Path $Path)) {
$MessageParams = @{
Message = "Could not add anything into the given CredentialStore."
ErrorAction = "Stop"
}
Write-Error @MessageParams
}
# Read the file content based on the given ParameterSetName
$CSContent = Get-CredentialStore -Shared -Path $Path
if ($Identifier -ne "") {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
}
else {
$CredentialName = $RemoteHost
}
if (Get-Member -InputObject $CSContent -Name $CredentialName -Membertype NoteProperty) {
# We need to use the .NET Method because there is no easier way in PowerShell.
$CSContent.PSObject.Properties.Remove($CredentialName)
ConvertTo-Json -InputObject $CSContent -Depth 5 | Out-File -FilePath $Path -Encoding utf8
}
else {
$MessageParams = @{
Message = "The given CredentialStoreItem does not exist."
}
Write-Warning @MessageParams
}
}
end {
}
} }

View File

@ -42,9 +42,6 @@ function Set-CredentialStoreItem {
[CmdletBinding(DefaultParameterSetName = "Private")] [CmdletBinding(DefaultParameterSetName = "Private")]
param( param(
[Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData,
[Parameter(Mandatory = $true, ParameterSetName = "Private")] [Parameter(Mandatory = $true, ParameterSetName = "Private")]
[Parameter(Mandatory = $true, ParameterSetName = "Shared")] [Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[string]$RemoteHost, [string]$RemoteHost,
@ -57,66 +54,91 @@ function Set-CredentialStoreItem {
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[PSCredential]$Credential, [PSCredential]$Credential,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[switch]$Shared [ValidateNotNullOrEmpty()]
[string]$Path
) )
# First set a constant path for private CredentialStore mode. begin {
if ($PSCmdlet.ParameterSetName -eq "Private") { # Set the CredentialStore for private, shared or custom mode.
$Path = "{0}\CredentialStore.json" -f $env:APPDATA Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
} if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = Get-DefaultCredentialStorePath
# Lets do a quick test on the given CredentialStore.
if (-not(Test-CredentialStore -Path $Path)) {
$MessageParams = @{
Message = "Could not add anything into the given CredentailStore."
ErrorAction = "Stop"
} }
Write-Error @MessageParams elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
} if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
# Read the file content based on the given ParameterSetName
$CSContent = Get-CredentialStore -Path $Path
$CurrentDate = Get-Date -UFormat "%Y-%m-%d %H:%M:%S"
if ($Identifier -ne "") {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
}
else {
$CredentialName = $RemoteHost
}
if (-not($Credential)) {
$Credential = Get-Credential -Message $CredentialName
}
if ($Credential.UserName) {
if ($CSContent.Type -eq "Shared") {
$Key = Get-ChallengeFile
$encypted = ConvertFrom-SecureString -SecureString $Credential.Password -Key $Key
}
else {
$encypted = ConvertFrom-SecureString -SecureString $Credential.Password
}
if (Get-Member -InputObject $CSContent -Name $CredentialName -Membertype Properties) {
$CSContent.$CredentialName.User = $Credential.UserName
$CSContent.$CredentialName.Password = $encypted
$CSContent.$CredentialName.Creation = $CurrentDate
ConvertTo-Json -InputObject $CSContent | Out-File -FilePath $Path
}
else {
$MessageParams = @{
Message = "The given CredentailStoreItem does not exist."
} }
Write-Warning @MessageParams
} }
} }
Else {
$MessageParams = @{ process {
Message = "Please Provide at least a valid user!" # Lets do a quick test on the given CredentialStore.
ErrorAction = "Stop" if (-not(Test-CredentialStore -Shared -Path $Path)) {
$MessageParams = @{
Message = "Could not add anything into the given CredentailStore."
ErrorAction = "Stop"
}
Write-Error @MessageParams
} }
Write-Error @MessageParams
# Read the file content based on the given ParameterSetName
$CSContent = Get-CredentialStore -Shared -Path $Path
$CurrentDate = Get-Date -UFormat "%Y-%m-%d %H:%M:%S"
if ($Identifier -ne "") {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
}
else {
$CredentialName = $RemoteHost
}
if (-not($Credential)) {
$Credential = Get-Credential -Message $CredentialName
}
if ($Credential.UserName) {
try {
$Cert = Get-PfxCertificate -FilePath $CSContent.PfxCertificate -ErrorAction Stop
}
catch {
$_.Exception.Message | Write-Error
$ErrorParams = @{
Message = 'Could not read the given PFX certificate.'
ErrorAction = 'Stop'
Exception = [System.Security.Cryptography.CryptographicException]::new()
}
Write-Error @ErrorParams
}
if (Get-Member -InputObject $CSContent -Name $CredentialName -Membertype Properties) {
$RSAKey = Get-RandomAESKey
$CSContent.$CredentialName.User = $Credential.UserName
$CSContent.$CredentialName.Password = ConvertFrom-SecureString -SecureString $Credential.Password -Key $RSAKey
$CSContent.$CredentialName.LastChange = $CurrentDate
$CSContent.$CredentialName.EncryptedKey = [Convert]::ToBase64String(
$Cert.PublicKey.Key.Encrypt(
$RSAKey,
[System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1
)
)
ConvertTo-Json -InputObject $CSContent -Depth 5 | Out-File -FilePath $Path -Encoding utf8
}
}
Else {
$MessageParams = @{
Message = "Please Provide at least a valid user!"
ErrorAction = "Stop"
}
Write-Error @MessageParams
}
}
end {
} }
} }

View File

@ -1,4 +1,4 @@
function Test-CredentialStoreItem() { function Test-CredentialStoreItem {
<# <#
.SYNOPSIS .SYNOPSIS
Checks if the given RemoteHost identifier combination exists in the credential store. Checks if the given RemoteHost identifier combination exists in the credential store.
@ -64,32 +64,48 @@ function Test-CredentialStoreItem() {
[switch]$Shared [switch]$Shared
) )
if ($PSCmdlet.ParameterSetName -eq "Private") { begin {
$Path = "{0}\CredentialStore.json" -f $env:APPDATA # Set the CredentialStore for private, shared or custom mode.
Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = Get-DefaultCredentialStorePath
}
elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
}
}
} }
if ($Identifier -ne "") { process {
$CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost if ($Identifier -ne "") {
} $CredentialName = $RemoteHost = "{0}/{1}" -f $Identifier, $RemoteHost
else {
$CredentialName = $RemoteHost
}
if (Test-CredentialStore -Path $Path) {
$CS = Get-CredentialStore -Path $Path
$CSMembers = Get-Member -InputObject $CS
if (($CSMembers.MemberType -eq "NoteProperty") -and ($CSMembers.Name -eq $CredentialName)) {
return $true
} }
else { else {
return $false $CredentialName = $RemoteHost
}
if (Test-CredentialStore -Shared -Path $Path) {
$CS = Get-CredentialStore -Shared -Path $Path
$CSMembers = Get-Member -InputObject $CS
if (($CSMembers.MemberType -eq "NoteProperty") -and ($CSMembers.Name -contains $CredentialName)) {
return $true
}
else {
return $false
}
}
else {
$MsgParams = @{
ErrorAction = "Stop"
Message = "The given credential store ({0}) does not exist!" -f $Path
}
Write-Error @MsgParams
} }
} }
else {
$MsgParams = @{ end {
ErrorAction = "Stop"
Message = "The given credential store ({0}) does not exist!" -f $Path
}
Write-Error @MsgParams
} }
} }

View File

@ -1,45 +1,36 @@
#
# Module manifest for module 'PSCredentialStore'
#
# Generated by: OCram85
#
# Generated on: 27.07.2017
#
@{ @{
# Script module or binary module file associated with this manifest. # Script module or binary module file associated with this manifest.
RootModule = 'PSCredentialStore' RootModule = 'PSCredentialStore.psm1'
# Version number of this module. # Version number of this module.
# Do not touch the version number. It gets replaced in the build process. ModuleVersion = '0.0.9999'
ModuleVersion = '0.0.0.9999'
# Supported PSEditions # Supported PSEditions
# CompatiblePSEditions = @() CompatiblePSEditions = 'Desktop', 'Core'
# ID used to uniquely identify this module # ID used to uniquely identify this module
GUID = '6800e192-9df8-4e30-b253-eb2c799bbe84' GUID = '6800e192-9df8-4e30-b253-eb2c799bbe84'
# Author of this module # Author of this module
Author = 'OCram85' Author = 'OCram85'
# Company or vendor of this module # Company or vendor of this module
CompanyName = '' CompanyName = ''
# Copyright statement for this module # Copyright statement for this module
Copyright = '(c) 2017 OCram85. All rights reserved.' Copyright = '(c) 2019 OCram85. All rights reserved.'
# Description of the functionality provided by this module # Description of the functionality provided by this module
Description = 'A simple credential manager to store and reuse multiple credential objects.' Description = 'A simple credential manager to store and reuse multiple credential objects.'
# Minimum version of the Windows PowerShell engine required by this module # Minimum version of the PowerShell engine required by this module
PowerShellVersion = '4.0' PowerShellVersion = '5.1'
# Name of the Windows PowerShell host required by this module # Name of the PowerShell host required by this module
# PowerShellHostName = '' # PowerShellHostName = ''
# Minimum version of the Windows PowerShell host required by this module # Minimum version of the PowerShell host required by this module
# PowerShellHostVersion = '' # PowerShellHostVersion = ''
# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. # Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only.
@ -70,32 +61,36 @@
# NestedModules = @() # NestedModules = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export. # Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
FunctionsToExport = @( FunctionsToExport = @(
# Connection Group # Certificate
'New-CRTAttribute',
'New-PfxCertificate',
'Use-PfxCertificate',
# Connection
'Connect-To', 'Connect-To',
'Disconnect-From', 'Disconnect-From',
'Test-CSConnection', 'Test-CSConnection',
# Item Group # Item
'Get-CredentialStoreItem', 'Get-CredentialStoreItem',
'Set-CredentialStoreItem',
'New-CredentialStoreItem', 'New-CredentialStoreItem',
'Remove-CredentialStoreItem', 'Remove-CredentialStoreItem',
'Set-CredentialStoreItem',
'Test-CredentialStoreItem', 'Test-CredentialStoreItem',
# Store Group # Store
'Get-CredentialStore', 'Get-CredentialStore',
'New-CredentialStore', 'New-CredentialStore',
'Test-CredentialStore' 'Test-CredentialStore',
'Update-CredentialStore'
) )
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export. # Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
CmdletsToExport = @() CmdletsToExport = @()
# Variables to export from this module # Variables to export from this module
VariablesToExport = '*' VariablesToExport = '*'
# Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export. # Aliases to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no aliases to export.
AliasesToExport = @() AliasesToExport = @()
# DSC resources to export from this module # DSC resources to export from this module
# DscResourcesToExport = @() # DscResourcesToExport = @()
@ -107,33 +102,40 @@
# FileList = @() # FileList = @()
# Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell. # Private data to pass to the module specified in RootModule/ModuleToProcess. This may also contain a PSData hashtable with additional module metadata used by PowerShell.
PrivateData = @{ PrivateData = @{
PSData = @{ PSData = @{
# Tags applied to this module. These help with module discovery in online galleries. # Tags applied to this module. These help with module discovery in online galleries.
Tags = @('CredentialStore', Tags = 'CredentialStore', 'CredentialManager'
'CredentialManager'
)
# A URL to the license for this module. # A URL to the license for this module.
LicenseUri = 'https://github.com/OCram85/PSCredentialStore/blob/master/LICENSE' LicenseUri = 'https://github.com/OCram85/PSCredentialStore/blob/master/LICENSE'
# A URL to the main website for this project. # A URL to the main website for this project.
ProjectUri = 'https://github.com/OCram85/PSCredentialStore' ProjectUri = 'https://github.com/OCram85/PSCredentialStore'
# A URL to an icon representing this module. # A URL to an icon representing this module.
# IconUri = '' IconUri = 'https://raw.githubusercontent.com/OCram85/PSCredentialStore/master/assets/logo256.png'
# ReleaseNotes of this module # ReleaseNotes of this module
ReleaseNotes = 'This is a pre-release version!. Do not use in production!' ReleaseNotes = 'This is a pre-release version!. Do not use in production!'
# Prerelease string of this module
Prerelease = 'alpha1'
# Flag to indicate whether the module requires explicit user acceptance for install/update
# RequireLicenseAcceptance = $false
# External dependent modules of this module
# ExternalModuleDependencies = @()
} # End of PSData hashtable } # End of PSData hashtable
} # End of PrivateData hashtable } # End of PrivateData hashtable
# HelpInfo URI of this module # HelpInfo URI of this module
HelpInfoURI = 'https://github.com/OCram85/PSCredentialStore' HelpInfoURI = 'https://github.com/OCram85/PSCredentialStore'
# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. # Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix.
# DefaultCommandPrefix = '' # DefaultCommandPrefix = ''

View File

@ -1,10 +1,18 @@
$Items = (Get-ChildItem -Path ("{0}\*.ps1" -f $PSScriptRoot ) -Recurse ).FullName | Where-Object { #region module-definition
#endregion module-definition
Set-Variable -Name "CSVersion" -Value "2.0.0" -Option Constant -Scope 'Script' -ErrorAction Stop
#region dot-sourcing
# dot-sourcing all module functions. The export is handled via manifest file.
$Items = (Get-ChildItem -Path (Join-Path -Path $PSScriptRoot -ChildPath '*.ps1') -Recurse ).FullName | Where-Object {
$_ -notmatch "(Classes|Init)" $_ -notmatch "(Classes|Init)"
} }
ForEach ($Item in $Items) { foreach ($Item in $Items) {
# Write-Verbose ("dot sourcing file {0}" -f $Item) # Write-Verbose ("dot sourcing file {0}" -f $Item)
. $Item . $Item
} }
#endregion dot-sourcing
# Exports are now controlled by module manifest
# Export-ModuleMember -Function *

View File

@ -0,0 +1,61 @@
function Get-DefaultCredentialStorePath {
<#
.SYNOPSIS
Returns the default CredentialStore path based on the current OS.
.DESCRIPTION
This is a low level helper function.
.INPUTS
[None]
.OUTPUTS
[string]
.EXAMPLE
$Path = Get-DefaultCredentialStorePath
.NOTES
File Name : Get-DefaultCredentialStorePath.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
[OutputType([string])]
param(
[Parameter(Mandatory = $false)]
[switch]$Shared
)
begin {}
process {
if ($Shared.IsPresent) {
if ($IsLinux) {
return Join-Path -Path '/var/opt' -ChildPath 'PSCredentialStore/CredentialStore.json'
}
if ($IsMacOS) {
return Join-Path -Path '/var/opt' -ChildPath 'PSCredentialStore/CredentialStore.json'
}
elseif (($isWindows) -or ($PSVersionTable.PSVersion.Major -lt 6) -or ($PSVersionTable.PSEdition -eq 'Desktop')) {
return Join-Path -Path $env:ProgramData -ChildPath 'PSCredentialStore/CredentialStore.json'
}
}
else {
if ($IsLinux) {
return Join-Path -Path $Env:HOME -ChildPath 'CredentialStore.json'
}
if ($IsMacOS) {
return Join-Path -Path $Env:HOME -ChildPath 'CredentialStore.json'
}
elseif (($isWindows) -or ($PSVersionTable.PSVersion.Major -lt 6) -or ($PSVersionTable.PSEdition -eq 'Desktop')) {
return Join-Path -Path $env:AppData -ChildPath 'CredentialStore.json'
}
}
}
end {}
}

View File

@ -0,0 +1,44 @@
function Get-RandomAESKey {
<#
.SYNOPSIS
Generate a new 32-byte AES key.
.DESCRIPTION
Uses the System.Security.Cryptography namespace for random aes key generation.
.INPUTS
[None]
.OUTPUTS
[byte[]]
.EXAMPLE
.\Get-RandomAESKey
.NOTES
File Name : Get-RandomAESKey.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
[OutputType([byte[]])]
param()
begin {}
process {
$key = [byte[]]::new(32)
$rng = [System.Security.Cryptography.RNGCryptoServiceProvider]::Create()
$rng.GetBytes($key)
Write-Output $key
if ($null -ne $key) {
[array]::Clear($key, 0, $key.Length)
}
}
end {}
}

View File

@ -0,0 +1,44 @@
function Get-TempDir {
<#
.SYNOPSIS
Returns the valid temp dir of the current OS
.DESCRIPTION
Returns the valid temp dir of the current OS.
.INPUTS
[None]
.OUTPUTS
[string]
.EXAMPLE
Get-TempDir
.NOTES
File Name : Get-TempDir.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
[OutputType([string])]
param()
begin {
}
process {
if ($IsLinux) {
return (Resolve-Path -Path '/tmp/').Path
}
if ($IsMacOS) {
return (Resolve-Path -Path '/tmp/').Path
}
elseif (($isWindows) -or ($PSVersionTable.PSVersion.Major -lt 6) -or ($PSVersionTable.PSEdition -eq 'Desktop')) {
return (Resolve-Path -Path $env:TEMP).Path
}
}
end {
}
}

View File

@ -35,7 +35,7 @@ function Test-Module {
.NOTES .NOTES
``` ```
File Name : Get-RandomKey.ps1 File Name : Test-Module.ps1
Author : Marco Blessing - marco.blessing@googlemail.com Author : Marco Blessing - marco.blessing@googlemail.com
Requires : Requires :
``` ```
@ -50,10 +50,6 @@ function Test-Module {
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string]$Name, [string]$Name,
[Parameter(Mandatory = $false)]
[ValidateSet('Module', 'PSSnapin', 'Custom')]
[string]$Type = 'Module',
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string]$MessagePattern = @" [string]$MessagePattern = @"
@ -62,45 +58,22 @@ Could not find the required {0} called {1}. Please install the required {0} to r
[Parameter(Mandatory = $false)] [Parameter(Mandatory = $false)]
[switch]$StopIfFails [switch]$StopIfFails
) )
begin { begin {}
}
process { process {
$Message = $MessagePattern -f $Type, $Name $Message = $MessagePattern -f $Type, $Name
Write-Debug $Message Write-Debug $Message
switch ($Type) {
'Module' {
if (Get-Module -Name $Name -ListAvailable) {
return $true
}
else {
if ($StopIfFails) {
Write-Error -Message $Message -ErrorAction Stop -Category NotInstalled
}
return $false
}
}
'PSSnapin' { if (Get-Module -Name $Name -ListAvailable) {
if (Get-PSSnapin -Name $Name -Registered -ErrorAction SilentlyContinue) { return $true
return $true }
} else {
else { if ($StopIfFails) {
if ($StopIfFails) { Write-Error -Message $Message -ErrorAction Stop -Category NotInstalled
Write-Error -Message $Message -ErrorAction Stop -Category NotInstalled
}
return $false
}
}
'Custom' {
Throw 'Custom tests are not implemented yet!'
} }
return $false
} }
} }
end { end {}
}
} }

View File

@ -35,36 +35,54 @@ function Get-CredentialStore {
#> #>
[CmdletBinding(DefaultParameterSetName = "Private")] [CmdletBinding(DefaultParameterSetName = "Private")]
[OutputType("PSCredentialStore.Store")]
param( param(
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [string]$Path,
[Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared [switch]$Shared
) )
if ($PSCmdlet.ParameterSetName -eq 'Private') { begin {}
$Path = "{0}\CredentialStore.json" -f $env:APPDATA
}
if (Test-CredentialStore -Path $Path) { process {
try { # Set the CredentialStore for private, shared or custom mode.
$FileContent = Get-Content -Path $Path -Raw Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
ConvertFrom-Json $FileContent if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = Get-DefaultCredentialStorePath
} }
catch [System.Exception] { elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
if (!($PSBoundParameters.ContainsKey('Path'))) {
$Path = Get-DefaultCredentialStorePath -Shared
}
}
if (Test-CredentialStore -Path $Path -Shared) {
try {
$FileContent = Get-Content -Path $Path -Raw
$CS = ConvertFrom-Json $FileContent
$CS.PSObject.TypeNames.Insert(0, "PSCredentialStore.Store")
return $CS
}
catch [System.Exception] {
$MessageParams = @{
Message = "Unknown CredentialStore format. Invalid JSON file."
ErrorAction = "Stop"
}
Write-Error @MessageParams
}
}
else {
$MessageParams = @{ $MessageParams = @{
Message = "Unknown CredentialStore format. Invalid JSON file." Message = "Could not find the CredentialStore."
ErrorAction = "Stop" ErrorAction = "Stop"
} }
Write-Error @MessageParams Write-Error @MessageParams
} }
} }
else {
$MessageParams = @{ end {}
Message = "Could not find the CredentialStore."
ErrorAction = "Stop"
}
Write-Error @MessageParams
}
} }

View File

@ -22,7 +22,8 @@ function New-CredentialStore {
[None] [None]
.OUTPUTS .OUTPUTS
[None] ['PSCredentialStore.Store'] Returns the recently created CredentialStore object if the -PassThru parameter
was given.
.EXAMPLE .EXAMPLE
New-CredentialStore New-CredentialStore
@ -50,63 +51,163 @@ function New-CredentialStore {
https://github.com/OCram85/PSCredentialStore https://github.com/OCram85/PSCredentialStore
#> #>
[CmdletBinding(DefaultParameterSetName = "Private")] [CmdletBinding(SupportsShouldProcess = $true, DefaultParameterSetName = "Private")]
[OutputType("PSCredentialStore.Store")]
param( param(
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared, [switch]$Shared,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData, [string]$Path,
[Parameter(Mandatory = $false, ParameterSetName = "Private")] [Parameter(Mandatory = $false, ParameterSetName = "Private")]
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[switch]$Force [switch]$Force,
[Parameter(Mandatory = $false, ParameterSetName = "Private")]
[Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[switch]$PassThru,
[Parameter(Mandatory = $false, ParameterSetName = "Private")]
[Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[Switch]$SkipPFXCertCreation
) )
# Lets get the current Date in a human readable format. begin {
$CurrentDate = Get-Date -UFormat "%Y-%m-%d %H:%M:%S" # Lets get the current Date in a human readable format.
$CurrentDate = Get-Date -UFormat "%Y-%m-%d %H:%M:%S"
# Set latest Credential Store version # Set latest Credential Store version
Set-Variable -Name "CSVersion" -Value "1.2.0" -Option Constant # Set-Variable -Name "CSVersion" -Value "2.0.0" -Option Constant -Scope
# Set the CredentialStore path for private mode.
Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
if ($PSCmdlet.ParameterSetName -eq "Private") {
$Path = "{0}\CredentialStore.json" -f $Env:APPDATA
} }
# Test if in the given store already a CredentialStore exists. process {
Write-Verbose "Test if there is already a credential store." # Set the CredentialStore for private, shared or custom mode.
if ((Test-CredentialStore -Path $Path) -and ($Force -ne $true)) { Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
$MessageParam = @{ if ($PSCmdlet.ParameterSetName -eq "Private") {
Message = "The given file already exists. Use the 'Force' switch to override the existing store." $Path = Get-DefaultCredentialStorePath
ErrorAction = "Stop"
} }
Write-Error @MessageParam elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
} if (!($PSBoundParameters.ContainsKey('Path'))) {
# We need to use the IDictionary to keep the property sorting in the object. $Path = Get-DefaultCredentialStorePath -Shared
$ObjProperties = [ordered]@{ }
Version = $CSVersion }
Creation = $CurrentDate
} # Test if in the CredentialStore already exists.
if ($PSCmdlet.ParameterSetName -eq "Shared") { Write-Verbose "Test if there is already a credential store."
$ObjProperties.Type = "Shared" if ((Test-Path -Path $Path) -and ($Force -ne $true)) {
# Check if a ChallengeFile already exists. We don't want to overide it. $ErrorParams = @{
# Otherwise previous created CredentialStores couln't be decrypted anymore. ErrorAction = 'Stop'
if (-not (Test-ChallengeFile)) { Exception = [System.InvalidOperationException]::new(
Set-ChallengeFile 'The given file already exists. Use the -Force switch to override the existing store.'
)
}
Write-Error @ErrorParams
}
if (! $SkipPFXCertCreation.IsPresent) {
$CRTParams = @{
Country = 'DE'
State = 'PSCredentialStore'
City = 'PSCredentialStore'
Organization = 'PSCredentialStore'
OrganizationalUnitName = ' '
CommonName = 'PrivateStore'
}
$CRTAttribute = New-CRTAttribute @CRTParams
# If we are working with a ne shared store we have to create the location first.
# Otherwise openssl fails with unknown path
$StoreHome = Split-Path -Path $Path -Parent
if (! (Test-Path -Path $StoreHome)) {
New-Item -ItemType Directory -Path $StoreHome -ErrorAction Stop
}
$PfxParams = @{
CRTAttribute = $CRTAttribute
KeyName = Join-Path -Path $StoreHome -ChildPath 'private.key'
CertName = Join-Path -Path $StoreHome -ChildPath 'PSCredentialStore.pfx'
ErrorAction = 'Stop'
Confirm = $false
}
if ((Test-Path $PfxParams.CertName) -and (! $Force.IsPresent)) {
$ErrorParams = @{
Exception = [System.IO.InvalidDataException]::new(
'There is already a PfxCertificate for a private CredentialStore!'
)
ErrorAction = 'Stop'
}
Write-Error @ErrorParams
}
try {
New-PfxCertificate @PfxParams
}
catch {
$_.Exception.Message | Write-Error
$ErrorParams = @{
ErrorAction = 'Stop'
Exception = [System.Exception]::new(
'Could not create the private PfXCertificate!'
)
}
Write-Error @ErrorParams
}
try {
$FreshCert = Get-PfxCertificate -FilePath $PfxParams.CertName -ErrorAction Stop
}
catch [System.Management.Automation.ItemNotFoundException] {
$_.Exception.Message | Write-Error
Write-Error -Message 'Could not read the new PfxCertificate.' -ErrorAction Stop
}
}
# We need to use the IDictionary to keep the property sorting in the object.
$ObjProperties = [ordered]@{
PSTypeName = 'PSCredentialStore.Store'
Version = $CSVersion
Created = $CurrentDate
PfxCertificate = $null
Thumbprint = $null
Type = $null
}
if (! $SkipPFXCertCreation.IsPresent) {
$ObjProperties.PfXCertificate = $PfxParams.CertName
$ObjProperties.Thumbprint = $FreshCert.Thumbprint
}
if ($PSCmdlet.ParameterSetName -eq "Shared") {
$ObjProperties.Type = "Shared"
}
else {
$ObjProperties.Type = "Private"
}
$CredentialStoreObj = [PSCustomObject]$ObjProperties
try {
$JSON = ConvertTo-Json -InputObject $CredentialStoreObj -ErrorAction Stop
$JSON | Out-File -FilePath $Path -ErrorAction Stop -Force
}
catch {
$_.Exception.Message | Write-Error
$ErrorParams = @{
ErrorAction = 'Stop'
Exception = [System.IO.IOException]::new(
'Unable to convert or write the CredentialStore'
)
}
Write-Error @ErrorParams
}
if ($PassThru.IsPresent) {
return $CredentialStoreObj
} }
} }
else {
$ObjProperties.Type = "Private" end {
}
# Create a new object for easy conversion into a json file
$CredentialStoreObj = New-Object -TypeName psobject -Property $ObjProperties
try {
ConvertTo-Json -InputObject $CredentialStoreObj | Out-File -FilePath $Path
}
catch [System.Exception] {
$_.Exception | Format-List -Force | Out-String | Write-Error -ErrorAction Stop
} }
} }

View File

@ -26,37 +26,40 @@ function Test-CredentialStore {
[CmdletBinding(DefaultParameterSetName = "Private")] [CmdletBinding(DefaultParameterSetName = "Private")]
param( param(
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $false, ParameterSetName = "Shared")]
[string]$Path = "{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData, [string]$Path,
[Parameter(Mandatory = $false, ParameterSetName = "Shared")] [Parameter(Mandatory = $true, ParameterSetName = "Shared")]
[switch]$Shared [switch]$Shared
) )
begin {
if ($PSCmdlet.ParameterSetName -eq "Private") { # Set latest Credential Store version
$Path = "{0}\CredentialStore.json" -f $Env:APPDATA #Set-Variable -Name "CSVersion" -Value "2.0.0" -Option Constant
} }
# Set latest Credential Store version process {
Set-Variable -Name "CSVersion" -Value "1.2.0" -Option Constant # Set the CredentialStore for private, shared or custom mode.
Write-Debug ("ParameterSetName: {0}" -f $PSCmdlet.ParameterSetName)
if (Test-Path $Path) { if ($PSCmdlet.ParameterSetName -eq "Private") {
Write-Verbose "CredentialStore in given path found." $Path = Get-DefaultCredentialStorePath
# try tor read the store. Removed the Get-CredentialStore function to avoid recursive calls.
try {
$FileContent = Get-Content -Path $Path -Raw
$CSContent = ConvertFrom-Json $FileContent
} }
catch { elseif ($PSCmdlet.ParameterSetName -eq "Shared") {
Write-Warning "Could not read or convert the given CredentialStore." if (!($PSBoundParameters.ContainsKey('Path'))) {
Return $False $Path = Get-DefaultCredentialStorePath -Shared
}
} }
Return $True Write-Verbose -Message ("Path is: {0}" -f $Path)
if (Test-Path $Path) {
Write-Verbose "CredentialStore in given path found."
return $true
}
else {
Write-Verbose "The given CredentialStore does not exist!"
return $false
}
} }
Else {
Write-Verbose "The given CredentialStore does not exist!" end {}
Return $False
}
} }

View File

@ -0,0 +1,140 @@
function Update-CredentialStore {
<#
.SYNOPSIS
A brief description of the function or script.
.DESCRIPTION
Describe the function of the script using a single sentence or more.
.PARAMETER One
Description of the Parameter (what it does)
.INPUTS
Describe the script input parameters (if any), otherwise it may also list the word "[None]".
.OUTPUTS
Describe the script output parameters (if any), otherwise it may also list the word "[None]".
.EXAMPLE
.\Remove-Some-Script.ps1 -One content
.NOTES
File Name : Update-CredentialStore.ps1
Author : Marco Blessing - marco.blessing@googlemail.com
Requires :
.LINK
https://github.com/OCram85/PSCredentialStore
#>
[CmdletBinding()]
[OutputType()]
param(
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[Version]$From = '1.2.0',
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[Version]$To = '2.0.0',
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$Path,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$PfxCertificate
)
begin {
}
process {
if (Test-Path -Path $Path) {
$CSOld = Get-CredentialStore -Shared -Path $Path -ErrorAction Stop
if ($CSOld.Version -ne $From) {
$ErrorParams = @{
Message = 'Can not migrate CredentialStore from version {0} to {1}' -f $From, $To
ErrorAction = 'Stop'
Exception = [System.Exception]::new()
}
Write-Error @ErrorParams
}
$CSNew = [PSCustomObject]@{
PSTypeName = 'PSCredentialStore.Store'
Version = $To
Created = $CurrentDate
PfxCertificate = $null
Thumbprint = $null
Type = $null
}
if ($PWD -eq (Get-DefaultCredentialStorePath)) {
$CSNew.Type = 'Private'
}
elseif ($PWD -eq (Get-DefaultCredentialStorePath -Shared)) {
$CSNew.Type = 'Shared'
}
else {
$ErrorParams = @{
Message = 'Can not determine a valid CredentialStore Type!'
ErrorAction = 'Stop'
Exception = [System.Exception]::new()
}
Write-Error @ErrorParams
}
$Cert = Get-PfxCertificate -FilePath $PfxCertificate -ErrorAction Stop
$CSNew.PfxCertificate = Join-Path -Path $PfxCertificate
$CSNew.Thumbprint = $Cert.Thumbprint
$CredentialItems = $CSOld | Get-Member -MemberType NoteProperty | Where-Object {
$_.Definition -like "*.PSCustomObject*"
} | Select-Object -ExpandProperty Name
# iterate through all existing items
foreach ($Item in $CredentialItems) {
$CurrentDate = Get-Date -UFormat "%Y-%m-%d %H:%M:%S"
$RSAKey = Get-RandomAESKey
$CredentialObj = [PSCustomObject]@{
User = $Item.UserName
Password = $null
Created = $CurrentDate
LastChange = $null
EncryptedKey = [Convert]::ToBase64String(
$Cert.PublicKey.Key.Encrypt(
$RSAKey,
[System.Security.Cryptography.RSAEncryptionPadding]::Pkcs1
)
)
}
if ($CSOld.Type -eq 'Private') {
$CredentialObject.Password = ConvertTo-SecureString -SecureString $Item.Password | ConvertFrom-SecureString -Key $RSAKey
}
elseif ($CSNew.Type -eq 'Shared') {
$ChallengeKey = [io.file]::ReadAllBytes((Join-Path -Path $PWD -ChildPath '/Challenge.bin'))
$CredentialObject.Password = ConvertTo-SecureString -SecureString $Item.Password -Key $ChallengeKey | ConvertFrom-SecureString -Key $RSAKey
}
Add-Member -InputObject $CSNew -Name (
($Item | Get-Variable).Name
) -MemberType NoteProperty -Value $CredentialObj
}
$CSNew | ConvertTo-Json -Depth 5 | Out-File -LiteralPath (
Join-Path -Path $PWD -ChildPath './CredentialStore.json'
) -Encoding utf8 -Confirm:$true
}
else {
$ErrorParams = @{
Message = 'Could not find the given CredentialStore path!'
ErrorAction = 'Stop'
Exception = [System.IO.FileNotFoundException]::new()
}
Write-Error @ErrorParams
}
}
end {
}
}

BIN
src/Vendor/libressl255/LICENSE (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libcrypto-41.dll (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libcrypto-41.exp (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libcrypto-41.lib (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libcrypto-41.pdb (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libssl-43.dll (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libssl-43.exp (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libssl-43.lib (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libssl-43.pdb (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libtls-15.dll (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libtls-15.exp (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libtls-15.lib (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/libtls-15.pdb (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/ocspcheck.exe (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
src/Vendor/libressl255/openssl.exe (Stored with Git LFS) vendored Normal file

Binary file not shown.

245
src/openssl.conf Normal file
View File

@ -0,0 +1,245 @@
#
# OpenSSL example configuration file.
# This is mostly being used for generation of certificate requests.
#
# This definition stops the following lines choking if HOME isn't
# defined.
HOME = .
RANDFILE = $ENV::HOME/.rnd
# Extra OBJECT IDENTIFIER info:
#oid_file = $ENV::HOME/.oid
oid_section = new_oids
# To use this configuration file with the "-extfile" option of the
# "openssl x509" utility, name here the section containing the
# X.509v3 extensions to use:
# extensions =
# (Alternatively, use a configuration file that has only
# X.509v3 extensions in its main [= default] section.)
[ new_oids ]
# We can add new OIDs in here for use by 'ca' and 'req'.
# Add a simple OID like this:
# testoid1=1.2.3.4
# Or use config file substitution like this:
# testoid2=${testoid1}.5.6
####################################################################
[ ca ]
default_ca = CA_default # The default ca section
####################################################################
[ CA_default ]
dir = ./demoCA # Where everything is kept
certs = $dir/certs # Where the issued certs are kept
crl_dir = $dir/crl # Where the issued crl are kept
database = $dir/index.txt # database index file.
new_certs_dir = $dir/newcerts # default place for new certs.
certificate = $dir/cacert.pem # The CA certificate
serial = $dir/serial # The current serial number
crl = $dir/crl.pem # The current CRL
private_key = $dir/private/cakey.pem # The private key
RANDFILE = $dir/private/.rand # private random number file
x509_extensions = usr_cert # The extentions to add to the cert
# Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs
# so this is commented out by default to leave a V1 CRL.
# crl_extensions = crl_ext
default_days = 365 # how long to certify for
default_crl_days = 30 # how long before next CRL
default_md = md5 # which md to use.
preserve = no # keep passed DN ordering
# A few difference way of specifying how similar the request should look
# For type CA, the listed attributes must be the same, and the optional
# and supplied fields are just that :-)
policy = policy_match
# For the CA policy
[ policy_match ]
countryName = match
stateOrProvinceName = match
organizationName = match
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
# For the 'anything' policy
# At this point in time, you must list all acceptable 'object'
# types.
[ policy_anything ]
countryName = optional
stateOrProvinceName = optional
localityName = optional
organizationName = optional
organizationalUnitName = optional
commonName = supplied
emailAddress = optional
####################################################################
[ req ]
default_bits = 2048
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString.
# utf8only: only UTF8Strings.
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: current versions of Netscape crash on BMPStrings or UTF8Strings
# so use this option with caution!
string_mask = nombstr
# req_extensions = v3_req # The extensions to add to a certificate request
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_default = DE
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = BW
localityName = Locality Name (eg, city)
localityName_default = PSCredentialStore
0.organizationName = Organization Name (eg, company)
0.organizationName_default = PSCredentialStore
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
organizationalUnitName = Organizational Unit Name (eg, section)
#organizationalUnitName_default = PSCRedentialStore
commonName = Common Name (eg, YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 40
# SET-ex3 = SET extension number 3
[ req_attributes ]
challengePassword = A challenge password
challengePassword_min = 0
challengePassword_max = 20
unstructuredName = An optional company name
[ usr_cert ]
# These extensions are added when 'ca' signs a request.
# This goes against PKIX guidelines but some CAs do it and some software
# requires this to avoid interpreting an end user certificate as a CA.
basicConstraints = CA:FALSE
# Here are some examples of the usage of nsCertType. If it is omitted
# the certificate can be used for anything *except* object signing.
# This is OK for an SSL server.
# nsCertType = server
# For an object signing certificate this would be used.
# nsCertType = objsign
# For normal client use this is typical
# nsCertType = client, email
# and for everything including object signing:
# nsCertType = client, email, objsign
# This is typical in keyUsage for a client certificate.
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, codeSigning
# This will be displayed in Netscape's comment listbox.
nsComment = "OpenSSL Generated Certificate"
# PKIX recommendations harmless if included in all certificates.
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid,issuer:always
# This stuff is for subjectAltName and issuerAltname.
# Import the email address.
# subjectAltName=email:copy
# Copy subject details
# issuerAltName=issuer:copy
#nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem
#nsBaseUrl
#nsRevocationUrl
#nsRenewalUrl
#nsCaPolicyUrl
#nsSslServerName
[ v3_req ]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment, codeSigning
[ v3_ca ]
# Extensions for a typical CA
# PKIX recommendation.
subjectKeyIdentifier=hash
authorityKeyIdentifier=keyid:always,issuer:always
# This is what PKIX recommends but some broken software chokes on critical
# extensions.
#basicConstraints = critical,CA:true
# So we do this instead.
basicConstraints = CA:true
# Key usage: this is typical for a CA certificate. However since it will
# prevent it being used as an test self-signed certificate it is best
# left out by default.
# keyUsage = cRLSign, keyCertSign
# Some might want this also
# nsCertType = sslCA, emailCA
# Include email address in subject alt name: another PKIX recommendation
# subjectAltName=email:copy
# Copy issuer details
# issuerAltName=issuer:copy
# DER hex encoding of an extension: beware experts only!
# obj=DER:02:03
# Where 'obj' is a standard or added object
# You can even override a supported extension:
# basicConstraints= critical, DER:30:03:01:01:FF
[ crl_ext ]
# CRL extensions.
# Only issuerAltName and authorityKeyIdentifier make any sense in a CRL.
# issuerAltName=issuer:copy
authorityKeyIdentifier = keyid:always,issuer:always

View File

@ -1,15 +1,17 @@
$RepoRoot = (Get-GitDirectory).replace('\.git', '') $RepoRoot = (Get-Item -Path (Get-GitDirectory) -Force).Parent | Select-Object -ExpandProperty 'FullName'
Write-Verbose -Message ('RepoRoot: {0}' -f $RepoRoot) -Verbose
$ManifestFilePath = Join-Path -Path $RepoRoot -ChildPath '/src/PSCredentialStore.psd1'
Write-Verbose -Message ("ManifestFilePath: {0}" -f $ManifestFilePath) -Verbose
Describe "Pre-Flight module tests" { Describe "Pre-Flight module tests" {
$ManifestFilePath = "{0}\src\PSCredentialstore.psd1" -f $RepoRoot
Context "Manifest file related" { Context "Manifest file related" {
It "Test the parsed file itsef" { It "Test the parsed file itself" {
{ Test-ModuleManifest -Path $ManifestFilePath } | Should -Not -Throw { Test-ModuleManifest -Path $ManifestFilePath -Verbose } | Should -Not -Throw
} }
} }
Context "Module consistency tests" { Context "Module consistency tests" {
IT "Importing should work" { It "Importing should work" {
{ Import-Module -Name $ManifestFilePath -Global -Force }| Should -Not -Throw { Import-Module -Name $ManifestFilePath -Global -Force -Verbose } | Should -Not -Throw
} }
} }
} }

View File

@ -1,60 +0,0 @@
#region HEADER
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
. (Get-ChildItem -Path $RepoRoot -Filter "Get-RandomKey.ps1" -Recurse).FullName
#endregion HEADER
Describe "Set-ChallengeFile" {
Context "Tests with custom path" {
It "Working dir and path not exist" {
{Set-ChallengeFile -Path 'C:\PSCredentialStore\Challenge.bin'} | Should -Not -Throw
}
It "No parameter and non file should return true" {
if (Test-Path -Path ("{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData)) {
Remove-Item -Path ("{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData)
}
Set-ChallengeFile
Test-Path -Path ("{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData) | Should -Be $true
}
It "Existing Credential file should return error" {
{ Set-ChallengeFile } | Should -Throw
Remove-Item -Path ("{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData)
}
It "Use -Force switch should create a new challenge file" {
# prepare for test and clean up old data
if (Test-Path -Path ("{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData)) {
Remove-Item -Path ("{0}\PSCredentialStore\Challenge.bin" -f $env:ProgramData)
}
Set-ChallengeFile
{ Set-ChallengeFile -Force } | Should -Not -Throw
}
It "Test directory creation for shared store" {
if (Test-Path -Path ("{0}\PSCredentialStore" -f $env:ProgramData)) {
Remove-Item -Path ("{0}\PSCredentialStore" -f $env:ProgramData) -Force -Recurse
}
Set-ChallengeFile
Test-Path -Path ("{0}\PSCredentialStore" -f $env:ProgramData) | Should -Be $true
}
}
Context "General Exception handling" {
Mock New-Item {throw "foobar exception"}
It "Test exception handling if the root directory could not be created" {
if (Test-Path -Path ("{0}\PSCredentialStore" -f $env:ProgramData)) {
Remove-Item -Path ("{0}\PSCredentialStore" -f $env:ProgramData) -Force -Recurse
}
{ Set-ChallengeFile } | Should -Throw "Could not create the parent data dir"
}
}
}

View File

@ -1,38 +0,0 @@
#region HEADER
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
#. (Get-ChildItem -Path $RepoRoot -Filter "Test-ChallengeFile.ps1" -Recurse).FullName
#endregion HEADER
Describe "Test-ChallengeFile" {
Context "Basic input tests" {
Mock Test-Path {return $true}
It "No parameter with existing challenge file" {
{Test-ChallengeFile} | Should -Not -Throw
}
It "No parameter and existing file should return true" {
Test-ChallengeFile | Should -Be $true
}
}
Context "Execute with parameter" {
$TestChFile = "{0}\resources\cs\Challenge.bin" -f $RepoRoot
It "Provide valid path" {
Test-ChallengeFile -Path $TestChFile | Should -Be $true
}
It "Provide fake path" {
Test-ChallengeFile -Path "C:\notexisting.bin" | Should -Be $false
}
}
}

View File

@ -1,25 +0,0 @@
#region HEADER
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
# . (Get-ChildItem -Path $RepoRoot -Filter "<FunctionName>.ps1" -Recurse).FullName
#endregion HEADER
Describe "Get-ModuleBase" {
Context "Basic syntax check" {
It "Test1: Should not throw" {
{ Get-ModuleBase } | Should -Not -Throw
}
}
}

View File

@ -1,39 +0,0 @@
#region HEADER
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
# . (Get-ChildItem -Path $RepoRoot -Filter "<FunctionName>.ps1" -Recurse).FullName
#endregion HEADER
Describe "Get-RandomKey" {
Context "Basic input tests" {
It "Test1: Should throw if wrong size is given" {
{Get-RandomKey -size 43} | Should -Throw
}
}
Context "Basic syntax check" {
It "Test1: Should return a key with a length of 16" {
$Key = Get-RandomKey -size 16
$Key.length | Should -Be 16
}
It "Test2: Should return a key with a length of 24" {
$Key = Get-RandomKey -size 24
$Key.length | Should -Be 24
}
It "Test3: Should return a key with a length of 32" {
$Key = Get-RandomKey -size 32
$Key.length | Should -Be 32
}
}
}

View File

@ -1,65 +0,0 @@
#region HEADER
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
#. (Get-ChildItem -Path $RepoRoot -Filter "<FunctionName>.ps1" -Recurse).FullName
#endregion HEADER
Describe "Test-ModuleName" {
Context "Basic input tests" {
It "Testing standard module should not throw" {
{ Test-Module -Name 'PowerShellGet' -Type Module } | Should -Not -Throw
}
It "Existing module should return true" {
Test-Module -Name 'PowerShellGet' -Type Module | Should -Be $true
}
}
Context "Custom Type tests" {
It "Using custom type should throw" {
{ Test-Module -Name "foobarr" -Type Custom} | Should -Throw
}
}
Context "Working with PSSnapins" {
It "Loading first PSSnaping should not throw " {
$Snap = Get-PSSnapin -Registered | Select-Object -First 1
{ Test-Module -Name $Snap.Name -Type PSSnapin } | Should -Not -Throw
}
It "Loading first PSSnaping should return true" {
$Snap = Get-PSSnapin -Registered | Select-Object -First 1
Test-Module -Name $Snap.Name -Type PSSnapin | Should -Be $true
}
It "Not existing PSSnaping should return false" {
Test-Module -Name 'foobar2000' -Type PSSnapin | Should -Be $false
}
It "StopifFails switch should thrown an error" {
{Test-Module -Name 'foobar2000' -Type PSSnapin -StopIfFails }| Should -Throw
}
}
Context "Working with modules" {
It "Loading first module should not throw " {
$Mod = Get-Module -ListAvailable | Select-Object -First 1
{ Test-Module -Name $Mod.Name -Type Module } | Should -Not -Throw
}
It "Loading first module should return true" {
$Snap = Get-Module -ListAvailable | Select-Object -First 1
Test-Module -Name $Snap.Name -Type Module | Should -Be $true
}
It "Not existing module should return false" {
Test-Module -Name 'foobar2000' -Type Module | Should -Be $false
}
It "StopifFails switch should thrown an error" {
{Test-Module -Name 'foobar2000' -Type Module -StopIfFails }| Should -Throw
}
}
}

View File

@ -1,113 +0,0 @@
#region HEADER
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
# $RepoRoot = (Get-Item -Path $here).Parent.Parent.FullName
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
#. (Get-ChildItem -Path $RepoRoot -Filter "Test-CredentialStore.ps1" -Recurse).FullName
#. (Get-ChildItem -Path $RepoRoot -Filter "New-CredentialStore.ps1" -Recurse).FullName
#. (Get-ChildItem -Path $RepoRoot -Filter "Get-CredentialStore.ps1" -Recurse).FullName
#. (Get-ChildItem -Path $RepoRoot -Filter "Get-CredentialStoreItem.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Test-ChallengeFile.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Get-ChallengeFile.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Set-ChallengeFile.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Get-RandomKey.ps1" -Recurse).FullName
#endregion HEADER
Describe "New-CredentialStoreItem" {
Context "Private Credential Store tests" {
It "Test1: Add entry to existing private store." {
If (-not (Test-CredentialStore)) {
New-CredentialStore
}
[String]$tmp = (65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_}
$tmp = $tmp.Replace(' ', '')
$tmpUser = "MyUser"
$tmpPwd = "fooobarysdfsfs" | ConvertTo-SecureString -AsPlainText -Force
$creds = New-Object -TypeName PsCredential -ArgumentList $tmpUser, $tmpPwd
New-CredentialStoreItem -RemoteHost $tmp -Credential $creds
# Had to remove the `{ <exp> } | Shoud Not Throw` because the return would be empty.
$content = Get-CredentialStoreItem -RemoteHost $tmp
$content.UserName | Should Be "MyUser"
#Cleanup Temp entry
$CS = Get-CredentialStore
$CS.PSObject.Properties.Remove($tmp)
ConvertTo-Json -InputObject $CS | Out-File -FilePath ("{0}\CredentialStore.json" -f $env:AppData)
}
}
Context "Test with new shared Credential Store" {
It "Test2: Create new RemoteHost entry" {
# prepare test environment
$tmpCS = 'C:\CredentialStore.json'
New-CredentialStore -Shared -Path $tmpCS
$UserName = "myuser"
$Password = ConvertTo-SecureString -String "mypasswd" -AsPlainText -Force
$mycreds = New-Object -TypeName PSCredential -ArgumentList $UserName, $Password
$RemoteHost = "foobar"
{ New-CredentialStoreItem -Path $tmpCS -RemoteHost $RemoteHost -Credential $mycreds -Shared } | Should Not Throw
$tmpCS = Get-Content -Path $tmpCS -Raw | ConvertFrom-Json
$res = Get-Member -InputObject $tmpCS -Name $RemoteHost -Membertype Properties
$res.Name | Should Be $RemoteHost
}
It "Adds Item with identifier to shared store" {
$tmpCS = 'C:\CredentialStore.json'
$UserName = "myuser"
$Password = ConvertTo-SecureString -String "mypasswd" -AsPlainText -Force
$mycreds = New-Object -TypeName PSCredential -ArgumentList $UserName, $Password
$RemoteHost = "foobar2"
New-CredentialStoreItem -Path $tmpCS -RemoteHost $RemoteHost -Credential $mycreds -Identifier 'Foo'
$writtenItem = Get-CredentialStoreItem -Path $tmpCS -RemoteHost $RemoteHost -Identifier 'Foo'
($writtenItem.UserName -eq $UserName) -and ($writtenItem.Password.Length -gt 0) | Should -Be $true
}
}
Context "Test optional parameter lookup" {
Mock Get-Credential {
$UserName = 'testuser'
$Password = ConvertTo-SecureString -String "mypasswd" -AsPlainText -Force
return [PSCredential]::new($UserName, $Password)
}
It "Test missing Credential" {
$tmpCS = 'C:\CredentialStore.json'
New-CredentialStoreItem -Path $tmpCs -Shared -RemoteHost 'foobar3'
$writtenItem = Get-CredentialStoreItem -Path $tmpCS -Shared -RemoteHost 'foobar3'
$writtenItem.UserName | Should -Be "testuser"
}
}
Context "General Exception handling" {
Mock Test-CredentialStore {return $false}
Mock Get-Credential {
$UserName = 'myUser'
$Password = ConvertTo-SecureString -String "mypasswd" -AsPlainText -Force
return [PSCredential]::new($UserName, $Password)
}
It "Missing CredentialStore should throw" {
{ New-CredentialStoreItem -Path 'C:\missingStore.json' -RemoteHost 'notrelevant' } | Should -Throw "Could not add anything"
}
}
Context "Testing pipeline paramter" {
It "Add the item with credential value from pipe" {
$UserName = 'pipeUser'
$Password = ConvertTo-SecureString -String "pipePasswd" -AsPlainText -Force
{ [PSCredential]::new($UserName, $Password) | New-CredentialStoreItem -RemoteHost 'PipeHost' } | Should -Not -Throw
}
It "Testing written item" {
(Get-CredentialStoreItem -RemoteHost 'PipeHost').UserName | Should -Be 'pipeUser'
}
}
}

View File

@ -0,0 +1,85 @@
Describe "New-CredentialStoreItem" {
Context "Private Credential Store tests" {
It "Test1: Add entry to existing private store." {
# Creat a fresh CredentialStore first
New-CredentialStore -Force
[String]$tmp = (65..90) + (97..122) | Get-Random -Count 5 | % {[char]$_}
$tmp = $tmp.Replace(' ', '')
$tmpUser = "MyUser"
$tmpPwd = "fooobarysdfsfs" | ConvertTo-SecureString -AsPlainText -Force
$creds = [PSCredential]::new($tmpUser, $tmpPwd)
New-CredentialStoreItem -RemoteHost $tmp -Credential $creds
# Had to remove the `{ <exp> } | Shoud Not Throw` because the return would be empty.
$content = Get-CredentialStoreItem -RemoteHost $tmp
$content.UserName | Should -Be "MyUser"
#Cleanup Temp entry
$CS = Get-CredentialStore
$CS.PSObject.Properties.Remove($tmp)
ConvertTo-Json -InputObject $CS | Out-File -FilePath (Get-DefaultCredentialStorePath)
}
}
Context "Test with new shared Credential Store" {
It "Test2: Create new RemoteHost entry" {
# prepare test environment
$tmpCS = Join-Path -Path (Get-TempDir) -ChildPath '/CredentialStore.json'
New-CredentialStore -Shared -Path $tmpCS -Force
$UserName = "myuser"
$Password = ConvertTo-SecureString -String "mypasswd" -AsPlainText -Force
$mycreds = [PSCredential]::new($UserName, $Password)
$RemoteHost = "foobar"
{ New-CredentialStoreItem -Shared -Path $tmpCS -RemoteHost $RemoteHost -Credential $mycreds } | Should -Not -Throw
$tmpCS = Get-Content -Path $tmpCS -Raw | ConvertFrom-Json
$res = Get-Member -InputObject $tmpCS -Name $RemoteHost -Membertype Properties
$res.Name | Should -Be $RemoteHost
}
It "Adds Item with identifier to shared store" {
$tmpCS = Join-Path -Path (Get-TempDir) -ChildPath '/CredentialStore.json'
New-CredentialStore -Shared -Path $tmpCS -Force
$UserName = "myuser"
$Password = ConvertTo-SecureString -String "mypasswd" -AsPlainText -Force
$mycreds = [PSCredential]::new($UserName, $Password)
$RemoteHost = "foobar2"
New-CredentialStoreItem -Shared -Path $tmpCS -RemoteHost $RemoteHost -Credential $mycreds -Identifier 'Foo'
$writtenItem = Get-CredentialStoreItem -Shared -Path $tmpCS -RemoteHost $RemoteHost -Identifier 'Foo'
($writtenItem.UserName -eq $UserName) -and ($writtenItem.Password.Length -gt 0) | Should -Be $true
}
}
Context "Test optional parameter lookup" {
It "Test missing Credential" {
function global:Get-Credential ([string]$Message) {
$UserName = 'testuser'
$Password = ConvertTo-SecureString -String "mypasswd" -AsPlainText -Force
return [PSCredential]::new($UserName, $Password)
}
$tmpCS = Join-Path -Path (Get-TempDir) -ChildPath '/CredentialStore.json'
New-CredentialStoreItem -Path $tmpCs -Shared -RemoteHost 'foobar3'
$writtenItem = Get-CredentialStoreItem -Path $tmpCS -Shared -RemoteHost 'foobar3'
$writtenItem.UserName | Should -Be "testuser"
Remove-Item -Path 'Function:\Get-Credential'
}
}
Context "General Exception handling" {
Mock Test-CredentialStore {return $false}
It "Missing CredentialStore should throw" {
{ New-CredentialStoreItem -Shared -Path 'C:\missingStore.json' -RemoteHost 'notrelevant' } | Should -Throw "Could not add anything"
}
}
Context "Testing pipeline paramter" {
It "Add the item with credential value from pipe" {
$UserName = 'pipeUser'
$Password = ConvertTo-SecureString -String "pipePasswd" -AsPlainText -Force
{ [PSCredential]::new($UserName, $Password) | New-CredentialStoreItem -RemoteHost 'PipeHost' } | Should -Not -Throw
}
It "Testing written item" {
(Get-CredentialStoreItem -RemoteHost 'PipeHost').UserName | Should -Be 'pipeUser'
}
}
}

View File

@ -0,0 +1,49 @@
Describe "Get-DefaultCredentialStorePath" {
Context "Basic syntax test" {
It "Test1: Should not throw" {
{ Get-DefaultCredentialStorePath } | Should -Not -Throw
}
}
Context "Private Type" {
It "Should return correct paths" {
$Path = Get-DefaultCredentialStorePath
#Write-Verbose -Message ('Delivered path is: {0}' -f $Path) -Verbose
if ($Env:APPVEYOR) {
$PathRef = Join-Path -Path $Env:APPDATA -ChildPath 'CredentialStore.json'
$Path | Should -Be $PathRef
}
elseif ($ENV:TRAVIS) {
if ($IsLinux) {
$PathRef = Join-Path -Path $Env:HOME -ChildPath 'CredentialStore.json'
$Path | Should -Be $PathRef
}
elseif ($IsMacOS) {
$PathRef = Join-Path -Path $Env:HOME -ChildPath 'CredentialStore.json'
$Path | Should -Be $PathRef
}
}
}
}
Context "Shared Type" {
It "Should return correct paths" {
$Path = Get-DefaultCredentialStorePath -Shared
#Write-Verbose -Message ('Delivered path is: {0}' -f $Path) -Verbose
if ($Env:APPVEYOR) {
$PathRef = Join-Path -Path $env:ProgramData -ChildPath 'PSCredentialStore/CredentialStore.json'
$Path | Should -Be $PathRef
}
elseif ($ENV:TRAVIS) {
if ($IsLinux) {
$PathRef = Join-Path -Path '/var/opt' -ChildPath 'PSCredentialStore/CredentialStore.json'
$Path | Should -Be $PathRef
}
elseif ($IsMacOS) {
$PathRef = Join-Path -Path '/var/opt' -ChildPath 'PSCredentialStore/CredentialStore.json'
$Path | Should -Be $PathRef
}
}
}
}
}

View File

@ -0,0 +1,7 @@
Describe "Get-ModuleBase" {
Context "Basic syntax check" {
It "Test1: Should not throw" {
{ Get-ModuleBase } | Should -Not -Throw
}
}
}

View File

@ -0,0 +1,13 @@
Describe "Get-RandomKey" {
Context "Basic input tests" {
It "Test1: Should not throw " {
{Get-RandomAESKey} | Should -Not -Throw
}
}
Context "Basic syntax check" {
It "Test2: Should return a key with a length of 32 bytes" {
$Key = Get-RandomAESKey
$Key.length | Should -Be 32
}
}
}

View File

@ -0,0 +1,27 @@
Describe "Get-TempDir" {
Context "Basic tests" {
It "Should not throw" {
{Get-TempDir} | Should -Not -Throw
}
It "Should return the correct os tmp path" {
$Path = Get-TempDir
if ($ENV:TRAVIS) {
if ($IsLinux) {
$RefPath = (Resolve-Path -Path '/tmp/').Path
$Path | Should -Be $RefPath
}
if ($IsMacOS) {
$RefPath = (Resolve-Path -Path '/tmp/').Path
$Path | Should -Be $RefPath
}
}
if ($Env:APPVEYOR) {
if (($isWindows) -or ($PSVersionTable.PSVersion.Major -lt 6) -or ($PSVersionTable.PSEdition -eq 'Desktop')) {
$RefPath = (Resolve-Path -Path $env:TEMP).Path
$Path | Should -Be $RefPath
}
}
}
}
}

View File

@ -1,27 +1,6 @@
#region HEADER
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
# $RepoRoot = (Get-Item -Path $here).Parent.Parent.FullName
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
. (Get-ChildItem -Path $RepoRoot -Filter "Get-ModuleBase.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Test-Module.ps1" -Recurse).FullName
#endregion HEADER
Describe "Resolve-Dependency" { Describe "Resolve-Dependency" {
Context "Basic syntax check" { Context "Basic syntax check" {
Mock Get-ModuleBase {return "{0}\resources" -f $PWD} Mock Get-ModuleBase {return (Join-Path -Path $PWD -ChildPath '/resources')}
Mock Test-Module {return $true} Mock Test-Module {return $true}
It "Test1: Should not throw" { It "Test1: Should not throw" {
{ Resolve-Dependency -Name 'foobar2000' } | Should -Not -Throw { Resolve-Dependency -Name 'foobar2000' } | Should -Not -Throw
@ -32,7 +11,10 @@ Describe "Resolve-Dependency" {
} }
Context "Enforce Error" { Context "Enforce Error" {
# Return incorrect module base to enforce there is no config file. # Return incorrect module base to enforce there is no config file.
Mock Get-ModuleBase {return "C:\"} Mock Get-ModuleBase {
if ($IsWindows) {return "C:\"}
elseif ($isLinux) {return "/"}
}
It "Missing dependency file should not cause an error" { It "Missing dependency file should not cause an error" {
{ Resolve-Dependency -Name 'awesome'} | Should -Not -Throw { Resolve-Dependency -Name 'awesome'} | Should -Not -Throw
} }
@ -42,7 +24,7 @@ Describe "Resolve-Dependency" {
} }
} }
Context "Testing input variations" { Context "Testing input variations" {
Mock Get-ModuleBase {return "{0}\resources" -f $PWD} Mock Get-ModuleBase {return (Join-Path -Path $PWD -ChildPath '/resources')}
It "Should return true if all given dependencies exist" { It "Should return true if all given dependencies exist" {
Resolve-Dependency -Name 'Existing' | Should -Be $true Resolve-Dependency -Name 'Existing' | Should -Be $true
} }

View File

@ -0,0 +1,26 @@
Describe "Test-ModuleName" {
Context "Basic input tests" {
It "Testing standard module should not throw" {
{ Test-Module -Name 'PowerShellGet' } | Should -Not -Throw
}
It "Existing module should return true" {
Test-Module -Name 'PowerShellGet' | Should -Be $true
}
}
Context "Working with modules" {
It "Loading first module should not throw " {
$Mod = Get-Module -ListAvailable | Select-Object -First 1
{ Test-Module -Name $Mod.Name } | Should -Not -Throw
}
It "Loading first module should return true" {
$Snap = Get-Module -ListAvailable | Select-Object -First 1
Test-Module -Name $Snap.Name | Should -Be $true
}
It "Not existing module should return false" {
Test-Module -Name 'foobar2000' | Should -Be $false
}
It "StopifFails switch should thrown an error" {
{Test-Module -Name 'foobar2000' -StopIfFails }| Should -Throw
}
}
}

View File

@ -1,47 +0,0 @@
#region HEADER
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
# $RepoRoot = (Get-Item -Path $here).Parent.Parent.FullName
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
#. (Get-ChildItem -Path $RepoRoot -Filter "Test-CredentialStore.ps1" -Recurse).FullName
#endregion HEADER
Describe "Get-CredentialStore" {
Context "Basic logic tests" {
$TestCredentialStore = Resolve-Path -Path ("{0}\resources\cs\CredentialStore.json" -f $RepoRoot)
It "Test1: Read CS without params" {
If (Test-Path -Path ("{0}\CredentialStore.json" -f $env:APPDATA)) {
{Get-CredentialStore} | Should Not Throw
}
Else {
Write-Warning "Default private Credential Store not found. Skipping..."
}
}
It "Test2: Read Credential Store with testing data" {
{Get-CredentialStore -Path $TestCredentialStore} | Should Not Throw
}
It "Test3: Not existing path should return false" {
{ Get-CredentialStore -Path 'C:\foobar\CredentialStore.json' -Shared }| Should -Throw "Could not find the CredentialStore."
}
}
Context "Testing invalid json data" {
Mock Test-CredentialStore {return $true}
Mock Get-Content {return '"foo":"bar",'}
It "Should throw with invalid CredentialStore" {
{ Get-Credentialstore -Path "C:\dummy.json"} | Should -Throw "Unknown CredentialStore format. Invalid JSON file."
}
}
}

View File

@ -1,120 +0,0 @@
#region HEADER
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
# $RepoRoot = (Get-Item -Path $here).Parent.Parent.FullName
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
#. (Get-ChildItem -Path $RepoRoot -Filter "Test-CredentialStore.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Test-ChallengeFile.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Set-ChallengeFile.ps1" -Recurse).FullName
. (Get-ChildItem -Path $RepoRoot -Filter "Get-RandomKey.ps1" -Recurse).FullName
#endregion HEADER
# Backup existing credential stores
$VerbosePreference = "Continue"
Write-Verbose "Backup private Credential Store..."
$CSPath = ("{0}\CredentialStore.json" -f $env:APPDATA)
$BackupFile = "{0}.back" -f $CSPath
If (Test-Path -Path $CSPath) {
Move-Item -Path $CSPath -Destination $BackupFile
}
Write-Verbose "Backup shared CredentialStore..."
$CSShared = ("{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData)
$BackupSharedFile = "{0}.back" -f $CSShared
If (Test-Path -Path $CSShared) {
Move-Item -Path $CSShared -Destination $BackupSharedFile
}
Write-Verbose "Remove old CredentialStore in Temp dir"
$CSTemp = "{0}\CredentialStore.json" -f $Env:TEMP
If (Test-Path -Path $CSTemp) {
Remove-Item -Path $CSTemp
}
$VerbosePreference = "SilentlyContinue"
Describe "New-CredentialStore" {
Context "Private CS tests" {
$pCS = Join-Path -Path $env:APPDATA -ChildPath "CredentialStore.json"
It "Test1: Create new private CredentialStore" {
New-CredentialStore
$result = Test-Path -Path $pCS
$CS = Get-Content -Path $pCS -Raw | ConvertFrom-Json
($result -eq $True) -and ($CS.Type -eq "Private") | Should Be $True
}
It "Test2: Try to override private Store" {
{New-CredentialStore} | Should Throw
}
It "Test3: Reset existing Credential Store" {
$now = Get-Date
$CS = Get-Content -Path $pCS -Raw | ConvertFrom-Json
$CSCreation = [DateTime]$CS.Creation
New-CredentialStore -Force
$now -gt $csCreation | Should Be $True
}
}
Context "Shared CS tests" {
$pCS = Join-Path -Path $env:ProgramData -ChildPath "PSCredentialStore\CredentialStore.json"
It "Test1: Create a new Shared Credential Store" {
New-CredentialStore -Shared
Test-Path -Path ("{0}\PSCredentialStore\CredentialStore.json" -f $env:ProgramData) | Should Be $True
}
It "Test2: Try to override existing shared CS" {
{New-CredentialStore -Shared} | Should Throw
}
It "Test3: Reset shared CredentialStore" {
$now = Get-Date
$CS = Get-Content -Path $pCS -Raw | ConvertFrom-Json
$CSCreation = [DateTime]$CS.Creation
New-CredentialStore -Force -Shared
$now -gt $csCreation | Should Be $True
}
}
Context "Custom Shared CS tests" {
$pCS = Join-Path -Path $env:TEMP -ChildPath "CredentialStore.json"
It "Test1: Create new custom shared" {
{New-CredentialStore -Path $pCS -Shared} | Should Not Throw
}
It "Test2: Try to override exiting one" {
{New-CredentialStore -Path $pCS -Shared} | Should Throw
}
It "Test3: Reset existing custom CredentialStore" {
{New-CredentialStore -Path $pCS -Shared -Force} | Should Not Throw
}
}
Context "Test exception handling" {
Mock Out-File {throw "foobar exception"}
It "JSON Converstion should fail and throw" {
{ New-CredentialStore -Path "C:\dummy.json"} | Should -Throw
}
}
}
# Cleanup test stores and restore existing ones.
$VerbosePreference = "Continue"
Write-Verbose "Restoring private CredentialStore"
If (Test-Path -Path $BackupFile) {
If (Test-Path -Path $CSPath) {
Remove-Item -Path $CSPath
Move-Item -Path $BackupFile -Destination $CSPath
}
}
Write-Verbose "Restoring shared CredentialStore"
If (Test-Path -Path $BackupSharedFile) {
If (Test-Path -Path $CSShared) {
Remove-Item -Path $CSShared
Move-Item -Path $BackupSharedFile -Destination $CSShared
}
}
$VerbosePreference = "SilentlyContinue"

View File

@ -1,49 +0,0 @@
#region HEADER
$here = Split-Path -Parent $MyInvocation.MyCommand.Path
# $RepoRoot = (Get-Item -Path $here).Parent.Parent.FullName
$RepoRoot = (Get-GitDirectory).replace('\.git', '')
$sut = (Split-Path -Leaf $MyInvocation.MyCommand.Path) -replace '\.Tests\.', '.'
$sut = $sut -replace "\d{2}`_", ''
$suthome = (Get-ChildItem -Path $RepoRoot -Exclude ".\tests\" -Filter $sut -Recurse).FullName
# Skip try loading the source file if it doesn't exists.
If ($suthome.Length -gt 0) {
. $suthome
}
Else {
Write-Warning ("Could not find source file {0}" -f $sut)
}
# load additional functions defined in the repository. Replace the expression <FunctionName>.
# . (Get-ChildItem -Path $RepoRoot -Filter "<Function-Name>.ps1" -Recurse).FullName
#endregion HEADER
Describe "Test-CredentialStore" {
Context "Basic logic tests" {
$TestCredentialStore = Resolve-Path -Path ("{0}\resources\cs\CredentialStore.json" -f $RepoRoot)
It "Test1: Should Not Throw" {
{ Test-CredentialStore -Path $TestCredentialStore } | Should Not Throw
}
It "Test2: Read valid CredentialStore" {
$res = Test-CredentialStore -Path $TestCredentialStore
$res | Should Be $True
}
It "Test3: Read a broken CredentialStore" {
$BrokenCS = Resolve-Path -Path ("{0}\resources\cs\Broken_CS.json" -f $RepoRoot)
$oWarningPreference = $WarningPreference
$WarningPreference = 'SilentlyContinue'
$res = Test-CredentialStore -Path $BrokenCS
$res | Should Be $False
$WarningPreference = $oWarningPreference
}
It "Test4: Not existing path should return false" {
Test-CredentialStore -Path 'C:\foobar\CredentialStore.json' | Should -Be $false
}
It "Test5: testing private CredentialStore path" {
if (Test-Path -Path ("{0}\CredentialStore.json" -f $env:APPDATA) ) {
Remove-Item -Path ("{0}\CredentialStore.json" -f $env:APPDATA)
}
Test-CredentialStore | Should -Be $false
}
}
}

View File

@ -0,0 +1,96 @@
# Backup existing credential stores
$VerbosePreference = "Continue"
Write-Verbose "Backup private Credential Store..."
$CSPath = Get-DefaultCredentialStorePath
$BackupFile = "{0}.back" -f $CSPath
If (Test-Path -Path $CSPath) {
Move-Item -Path $CSPath -Destination $BackupFile
}
Write-Verbose "Backup shared CredentialStore..."
$CSShared = Get-DefaultCredentialStorePath -Shared
$BackupSharedFile = "{0}.back" -f $CSShared
If (Test-Path -Path $CSShared) {
Move-Item -Path $CSShared -Destination $BackupSharedFile
}
Write-Verbose "Remove old CredentialStore in Temp dir"
$CSTemp = Join-Path -Path (Get-TempDir) -ChildPath '/CredentialStore.json'
If (Test-Path -Path $CSTemp) {
Remove-Item -Path $CSTemp
}
$VerbosePreference = "SilentlyContinue"
Describe "New-CredentialStore" {
Context "Private CS tests" {
$pCS = Get-DefaultCredentialStorePath
It "Test1: Create new private CredentialStore" {
{ New-CredentialStore -Confirm:$false } | Should -Not -Throw
$result = Test-Path -Path $pCS
$CS = Get-Content -Path $pCS -Raw | ConvertFrom-Json
($result -eq $true) -and ($CS.Type -eq "Private") | Should -Be $true
}
It "Test2: Try to override private Store" {
{ New-CredentialStore -Confirm:$false } | Should -Throw
}
It "Test3: Reset existing Credential Store" {
$now = Get-Date
$CS = Get-Content -Path $pCS -Raw | ConvertFrom-Json
$CSCreation = [DateTime]$CS.Created
New-CredentialStore -Confirm:$false -Force
$now -gt $csCreation | Should -Be $true
}
}
Context "Shared CS tests" {
$sCS = Get-DefaultCredentialStorePath -Shared
It "Test1: Create a new Shared Credential Store" {
{ New-CredentialStore -Confirm:$false -Shared } | Should -Not -Throw
Test-Path -Path $sCS | Should -Be $true
}
It "Test2: Try to override existing shared CS" {
{New-CredentialStore -Shared -Confirm:$false} | Should -Throw
}
It "Test3: Reset shared CredentialStore" {
$now = Get-Date
$CS = Get-Content -Path $sCS -Raw | ConvertFrom-Json
$CSCreation = [DateTime]$CS.Created
New-CredentialStore -Force -Shared -Confirm:$false
$now -gt $csCreation | Should -Be $true
}
}
Context "Custom Shared CS tests" {
$cCS = Join-Path -Path (Get-TempDir) -ChildPath "CredentialStore.json"
It "Test1: Create new custom shared" {
{New-CredentialStore -Path $cCS -Shared -Confirm:$false} | Should -Not -Throw
}
It "Test2: Try to override exiting one" {
{New-CredentialStore -Path $cCS -Shared -Confirm:$false} | Should -Throw
}
It "Test3: Reset existing custom CredentialStore" {
{New-CredentialStore -Path $cCS -Shared -Force -Confirm:$false} | Should -Not -Throw
}
}
Context "Test exception handling" {
Mock Out-File {throw "foobar exception"}
It "JSON Conversion should fail and throw" {
{ New-CredentialStore -Path (Join-Path -Path (Get-TempDir) -ChildPath '/dummy.json') -Shared -Confirm:$false} | Should -Throw
}
}
}
# Cleanup test stores and restore existing ones.
$VerbosePreference = "Continue"
Write-Verbose "Restoring private CredentialStore"
If (Test-Path -Path $BackupFile) {
If (Test-Path -Path $CSPath) {
Remove-Item -Path $CSPath
Move-Item -Path $BackupFile -Destination $CSPath
}
}
Write-Verbose "Restoring shared CredentialStore"
If (Test-Path -Path $BackupSharedFile) {
If (Test-Path -Path $CSShared) {
Remove-Item -Path $CSShared
Move-Item -Path $BackupSharedFile -Destination $CSShared
}
}
$VerbosePreference = "SilentlyContinue"

View File

@ -0,0 +1,36 @@
$RepoRoot = (Get-Item -Path (Get-GitDirectory) -Force).Parent | Select-Object -ExpandProperty 'FullName'
Describe "Test-CredentialStore" {
Context "Basic logic tests" {
$TestCredentialStore = Join-Path -Path $RepoRoot -ChildPath '/resources/cs/CredentialStore.json'
It "Test1: Should Not Throw" {
{ Test-CredentialStore -Shared -Path $TestCredentialStore } | Should -Not -Throw
}
It "Test2: Read valid CredentialStore" {
$res = Test-CredentialStore -Shared -Path $TestCredentialStore
$res | Should -Be $true
}
It "Test3: Read a broken CredentialStore" {
$BrokenCS = Join-Path -Path $RepoRoot -ChildPath '{0}/resources/cs/Broken_CS.json'
$oWarningPreference = $WarningPreference
$WarningPreference = 'SilentlyContinue'
$res = Test-CredentialStore -Shared -Path $BrokenCS
$res | Should -Be $false
$WarningPreference = $oWarningPreference
}
It "Test4: Not existing path should return false" {
if ($isWindows -or ($PSVersionTable.PSVersion.Major -eq 5)) {
Test-CredentialStore -Shared -Path 'C:\foobar\CredentialStore.json' | Should -Be $false
}
elseif ($isWindows -or $IsMacOS) {
Test-CredentialStore -Shared -Path '/var/opt/foo.json' | Should -Be $false
}
}
It "Test5: testing private CredentialStore path" {
if (Test-Path -Path (Get-DefaultCredentialStorePath)) {
Remove-Item -Path (Get-DefaultCredentialStorePath)
}
Test-CredentialStore | Should -Be $false
}
}
}

View File

@ -0,0 +1,32 @@
$RepoRoot = (Get-Item -Path (Get-GitDirectory) -Force).Parent | Select-Object -ExpandProperty 'FullName'
Describe "Get-CredentialStore" {
Context "Basic logic tests" {
$TestCredentialStore = Join-Path -Path $RepoRoot -ChildPath 'resources/cs/CredentialStore.json'
$TestPfxCert = Join-Path -Path $RepoRoot -ChildPath 'resources/cs/PSCredentialStore.pfx'
'TestCredentialStore: {0}' -f $TestCredentialStore
It "Test1: Read CS without params" {
if (! (Test-Path -Path (Get-DefaultCredentialStorePath)) ) {
{ New-CredentialStore -Force } | Should -Not -Throw
}
{ Get-CredentialStore } | Should -Not -Throw
}
It "Test2: Read Credential Store with testing data" {
{ Use-PfxCertificate -Shared -CredentialStore $TestCredentialStore -Path $TestPfxCert } | Should -Not -Throw
{ Get-CredentialStore -Shared -Path $TestCredentialStore } | Should -Not -Throw
}
It "Test3: Not existing path should return false" {
{ Get-CredentialStore -Shared -Path './CredentialStore.json' }| Should -Throw "Could not find the CredentialStore."
}
}
Context "Testing invalid json data" {
#Mock Test-CredentialStore {return $true}
#Mock Get-Content {return '"foo":"bar",'}
$BrokenCS = Join-Path -Path $RepoRoot -ChildPath 'resources/cs/Broken_CS.json'
Write-Verbose -Message ('BrokenCS Path: {0}' -f $BrokenCS) -Verbose
It "Should throw with invalid CredentialStore" {
{ Get-CredentialStore -Path -Shared $BrokenCS } | Should -Throw
}
}
}

View File

@ -7,7 +7,7 @@
- Module name - Module name
#> #>
$CALLSIGN = 'PSCredentialStore' $CALLSIGN = 'PSCredentialStore'
Write-Host ("Callsign is: {0}" -f $CALLSIGN) -ForegroundColor Yellow Write-Host ("Callsign is: {0}" -f $CALLSIGN) -ForegroundColor Black -BackgroundColor Yellow
Function Invoke-InstallDependencies() { Function Invoke-InstallDependencies() {
@ -16,18 +16,19 @@ Function Invoke-InstallDependencies() {
Process { Process {
Try { Try {
Get-PackageProvider -ListAvailable
Install-PackageProvider -Name NuGet -RequiredVersion '2.8.5.208' -Force -Verbose Install-PackageProvider -Name NuGet -RequiredVersion '2.8.5.208' -Force -Verbose
Import-PackageProvider -Name NuGet -RequiredVersion '2.8.5.208' -Force Import-PackageProvider -Name NuGet -RequiredVersion '2.8.5.208' -Force
Install-Module -Name 'Pester' -Scope CurrentUser -RequiredVersion '4.0.8' -Force -SkipPublisherCheck -AllowClobber Install-Module -Name 'Pester' -Scope CurrentUser -RequiredVersion '4.4.2' -Force -SkipPublisherCheck -AllowClobber
Install-Module -Name 'posh-git' -Scope CurrentUser -RequiredVersion '0.7.1' -Force -SkipPublisherCheck -AllowClobber Install-Module -Name 'posh-git' -Scope CurrentUser -RequiredVersion '1.0.0-beta2' -Force -SkipPublisherCheck -AllowClobber -AllowPrerelease
Install-Module -Name 'PSCoverage' -Scope CurrentUser -Force -SkipPublisherCheck -AllowClobber # Install-Module -Name 'PSCoverage' -Scope CurrentUser -Force -SkipPublisherCheck -AllowClobber
Import-Module -Name 'Pester', 'posh-git', 'PSCoverage' Import-Module -Name 'Pester', 'posh-git' #, 'PSCoverage'
} }
Catch { Catch {
$MsgParams = @{ $MsgParams = @{
Message = 'Could not install the required dependencies!' Message = 'Could not install the required dependencies!'
Category = 'Error' Category = 'Error'
Details = $_.Exception.Message Details = $_.Exception.Message
} }
Add-AppveyorMessage @MsgParams Add-AppveyorMessage @MsgParams
Throw $MsgParams.Message Throw $MsgParams.Message
@ -39,21 +40,21 @@ Function Invoke-AppVeyorBumpVersion() {
[CmdletBinding()] [CmdletBinding()]
Param() Param()
Write-Host "Listing Env Vars for debugging:" -ForegroundColor Yellow Write-Host "Listing Env Vars for debugging:" -ForegroundColor Black -BackgroundColor Yellow
# Filter Results to prevent exposing secure vars. # Filter Results to prevent exposing secure vars.
Get-ChildItem -Path "Env:*" | Where-Object { $_.name -notmatch "(NuGetToken|CoverallsToken)"} | Sort-Object -Property Name | Format-Table Get-ChildItem -Path "Env:*" | Where-Object { $_.name -notmatch "(NuGetToken|CoverallsToken)"} | Sort-Object -Property Name | Format-Table
Try { Try {
$ModManifest = Get-Content -Path (".\src\{0}.psd1" -f $CALLSIGN) $ModManifest = Get-Content -Path (".\src\{0}.psd1" -f $CALLSIGN)
$BumpedManifest = $ModManifest -replace '0.0.0.9999', $Env:APPVEYOR_BUILD_VERSION $BumpedManifest = $ModManifest -replace '0.0.9999', $Env:APPVEYOR_BUILD_VERSION
Remove-Item -Path (".\src\{0}.psd1" -f $CALLSIGN) Remove-Item -Path (".\src\{0}.psd1" -f $CALLSIGN)
Out-File -FilePath (".\src\{0}.psd1" -f $CALLSIGN) -InputObject $BumpedManifest -NoClobber -Encoding utf8 -Force Out-File -FilePath (".\src\{0}.psd1" -f $CALLSIGN) -InputObject $BumpedManifest -NoClobber -Encoding utf8 -Force
} }
Catch { Catch {
$MsgParams = @{ $MsgParams = @{
Message = 'Could not bump current version into module manifest.' Message = 'Could not bump current version into module manifest.'
Category = 'Error' Category = 'Error'
Details = $_.Exception.Message Details = $_.Exception.Message
} }
Add-AppveyorMessage @MsgParams Add-AppveyorMessage @MsgParams
Throw $MsgParams.Message Throw $MsgParams.Message
@ -62,27 +63,29 @@ Function Invoke-AppVeyorBumpVersion() {
Function Invoke-AppVeyorBuild() { Function Invoke-AppVeyorBuild() {
[CmdletBinding()] [CmdletBinding()]
[OutputType([PsCustomObject])]
Param() Param()
$MsgParams = @{ $MsgParams = @{
Message = 'Creating build artifacts' Message = 'Creating build artifacts'
Category = 'Information' Category = 'Information'
Details = 'Extracting source files and compressing them into zip file.' Details = 'Extracting source files and compressing them into zip file.'
} }
Add-AppveyorMessage @MsgParams Add-AppveyorMessage @MsgParams
$CompParams = @{ $CompParams = @{
Path = "{0}\src\*" -f $env:APPVEYOR_BUILD_FOLDER Path = "{0}\src\*" -f $env:APPVEYOR_BUILD_FOLDER
DestinationPath = "{0}\bin\{1}.zip" -f $env:APPVEYOR_BUILD_FOLDER, $CALLSIGN DestinationPath = "{0}\bin\{1}.zip" -f $env:APPVEYOR_BUILD_FOLDER, $CALLSIGN
Update = $True Update = $True
Verbose = $True Verbose = $True
} }
Compress-Archive @CompParams Compress-Archive @CompParams
$MsgParams = @{ $MsgParams = @{
Message = 'Pushing artifacts' Message = 'Pushing artifacts'
Category = 'Information' Category = 'Information'
Details = 'Pushing artifacts to AppVeyor store.' Details = 'Pushing artifacts to AppVeyor store.'
} }
Add-AppveyorMessage @MsgParams Add-AppveyorMessage @MsgParams
Push-AppveyorArtifact (".\bin\{0}.zip" -f $CALLSIGN) $ArtifactPath = Join-Path -Path '.' -ChildPath ('bin/{0}.zip' -f $CALLSIGN)
Push-AppveyorArtifact $ArtifactPath
} }
Function Invoke-AppVeyorTests() { Function Invoke-AppVeyorTests() {
@ -90,60 +93,85 @@ Function Invoke-AppVeyorTests() {
Param() Param()
$MsgParams = @{ $MsgParams = @{
Message = 'Starting Pester tests' Message = 'Starting Pester tests'
Category = 'Information' Category = 'Information'
Details = 'Now running all test found in .\tests\ dir.' Details = 'Now running all test found in .\tests\ dir.'
} }
Add-AppveyorMessage @MsgParams Add-AppveyorMessage @MsgParams
$testresults = Invoke-Pester -Path ( Get-ChildItem -Path ".\tests\*.Tests.ps1" -Recurse | Sort-Object -Property Name ) -ExcludeTag 'Disabled' -PassThru
ForEach ($Item in $testresults.TestResult) { try {
Write-Host '===== Preload internal private functions =====' -ForegroundColor Black -BackgroundColor Yellow
$Privates = Get-ChildItem -Path (Join-Path -Path $Env:APPVEYOR_BUILD_FOLDER -ChildPath '/src/Private/*') -Include "*.ps1" -Recurse
foreach ($File in $Privates) {
if (Test-Path -Path $File.FullName) {
. $File.FullName
Write-Verbose -Message ('Private function dot-sourced: {0}' -f $File.FullName) -Verbose
}
else {
Write-Warning -Message ('Could not find file: {0} !' -f $File.FullName)
}
}
}
catch {
$_.Exception.Message | Write-Error
throw 'Could not load required private functions!'
}
#$testresults = Invoke-Pester -Path ( Get-ChildItem -Path ".\tests\*.Tests.ps1" -Recurse | Sort-Object -Property Name ) -ExcludeTag 'Disabled' -PassThru
$srcFiles = Get-ChildItem -Path ".\src\*.ps1" -Recurse | Sort-Object -Property 'Name' | Select-Object -ExpandProperty 'FullName'
$testFiles = Get-ChildItem -Path ".\tests\*.Tests.ps1" -Recurse | Sort-Object -Property 'Name' | Select-Object -ExpandProperty 'FullName'
$TestResults = Invoke-Pester -Path $testFiles -CodeCoverage $srcFiles -PassThru
ForEach ($Item in $TestResults.TestResult) {
Switch ($Item.Result) { Switch ($Item.Result) {
"Passed" { "Passed" {
$TestParams = @{ $TestParams = @{
Name = "{0}: {1}" -f $Item.Context, $Item.Name Name = "{0}: {1}" -f $Item.Context, $Item.Name
Framework = "NUnit" Framework = "NUnit"
Filename = $Item.Describe Filename = $Item.Describe
Outcome = "Passed" Outcome = "Passed"
Duration = $Item.Time.Milliseconds Duration = $Item.Time.Milliseconds
} }
Add-AppveyorTest @TestParams Add-AppveyorTest @TestParams
} }
"Failed" { "Failed" {
$TestParams = @{ $TestParams = @{
Name = "{0}: {1}" -f $Item.Context, $Item.Name Name = "{0}: {1}" -f $Item.Context, $Item.Name
Framework = "NUnit" Framework = "NUnit"
Filename = $Item.Describe Filename = $Item.Describe
Outcome = "Failed" Outcome = "Failed"
Duration = $Item.Time.Milliseconds Duration = $Item.Time.Milliseconds
ErrorMessage = $Item.FailureMessage ErrorMessage = $Item.FailureMessage
ErrorStackTrace = $Item.StackTrace ErrorStackTrace = $Item.StackTrace
} }
Add-AppveyorTest @TestParams Add-AppveyorTest @TestParams
} }
Default { Default {
$TestParams = @{ $TestParams = @{
Name = "{0}: {1}" -f $Item.Context, $Item.Name Name = "{0}: {1}" -f $Item.Context, $Item.Name
Framework = "NUnit" Framework = "NUnit"
Filename = $Item.Describe Filename = $Item.Describe
Outcome = "None" Outcome = "None"
Duration = $Item.Time.Milliseconds Duration = $Item.Time.Milliseconds
ErrorMessage = $Item.FailureMessage ErrorMessage = $Item.FailureMessage
ErrorStackTrace = $Item.StackTrace ErrorStackTrace = $Item.StackTrace
} }
Add-AppveyorTest @TestParams Add-AppveyorTest @TestParams
} }
} }
} }
If ($testresults.FailedCount -gt 0) { If ($TestResults.FailedCount -gt 0) {
$MsgParams = @{ $MsgParams = @{
Message = 'Pester Tests failed.' Message = 'Pester Tests failed.'
Category = 'Error' Category = 'Error'
Details = "$($testresults.FailedCount) tests failed." Details = "$($TestResults.FailedCount) tests failed."
} }
Add-AppveyorMessage @MsgParams Add-AppveyorMessage @MsgParams
Throw $MsgParams.Message Throw $MsgParams.Message
} }
return $TestResults.CodeCoverage
} }
Function Invoke-CoverageReport() { Function Invoke-CoverageReport() {
@ -151,13 +179,17 @@ Function Invoke-CoverageReport() {
Param( Param(
[Parameter(Mandatory = $False)] [Parameter(Mandatory = $False)]
[ValidateNotNullOrEmpty()] [ValidateNotNullOrEmpty()]
[String]$RepoToken = $Env:CoverallsToken [String]$RepoToken = $Env:CoverallsToken,
[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[PSCustomObject]$PesterCoverageReport
) )
$FileMap = New-PesterFileMap -SourceRoot '.\src' -PesterRoot '.\tests' #$CoverageReport | Format-Custom -Depth 5 | Out-String | Write-Verbose
$CoverageReport = New-CoverageReport -PesterFileMap $FileMap -RepoToken $RepoToken $CoverageReport = New-CoverageReport -CodeCoverage $PesterCoverageReport -RepoToken $RepoToken
Write-Host "CoverageReport JSON:" -ForegroundColor Yellow Write-Host "CoverageReport JSON:" -ForegroundColor Yellow
$CoverageReport | Out-String | Write-Host #$CoverageReport | ConvertTo-Json -Depth 5 | Out-String | Write-Verbose
Publish-CoverageReport -CoverageReport $CoverageReport Publish-CoverageReport -CoverageReport $CoverageReport
} }
@ -170,36 +202,36 @@ Function Invoke-AppVeyorPSGallery() {
) )
Expand-Archive -Path (".\bin\{0}.zip" -f $CALLSIGN) -DestinationPath ("C:\Users\appveyor\Documents\WindowsPowerShell\Modules\{0}\" -f $CALLSIGN) -Verbose Expand-Archive -Path (".\bin\{0}.zip" -f $CALLSIGN) -DestinationPath ("C:\Users\appveyor\Documents\WindowsPowerShell\Modules\{0}\" -f $CALLSIGN) -Verbose
Import-Module -Name $CALLSIGN -Verbose -Force Import-Module -Name $CALLSIGN -Verbose -Force
Write-Host "Available Package Provider:" -ForegroundColor Yellow Write-Host "Available Package Provider:" -ForegroundColor Black -BackgroundColor Yellow
Get-PackageProvider -ListAvailable Get-PackageProvider -ListAvailable
Write-Host "Available Package Sources:" -ForegroundColor Yellow Write-Host "Available Package Sources:" -ForegroundColor Black -BackgroundColor Yellow
Get-PackageSource Get-PackageSource
Try { Try {
Write-Host "Try to get NuGet Provider:" -ForegroundColor Yellow Write-Host "Try to get NuGet Provider:" -ForegroundColor Black -BackgroundColor Yellow
Get-PackageProvider -Name NuGet -ErrorAction Stop Get-PackageProvider -Name NuGet -ErrorAction Stop
} }
Catch { Catch {
Write-Host "Installing NuGet..." -ForegroundColor Yellow Write-Host "Installing NuGet..." -ForegroundColor Black -BackgroundColor Yellow
Install-PackageProvider -Name NuGet -MinimumVersion '2.8.5.201' -Force -Verbose Install-PackageProvider -Name NuGet -MinimumVersion '2.8.5.201' -Force -Verbose
Import-PackageProvider NuGet -MinimumVersion '2.8.5.201' -Force Import-PackageProvider NuGet -MinimumVersion '2.8.5.201' -Force
} }
Try { Try {
If ($env:APPVEYOR_REPO_BRANCH -eq 'master') { If ($env:APPVEYOR_REPO_BRANCH -eq 'master') {
Write-Host "try to publish module" -ForegroundColor Yellow Write-Host "try to publish module" -ForegroundColor Black -BackgroundColor Yellow
Write-Host ("Callsign is: {0}" -f $CALLSIGN) -ForegroundColor Yellow Write-Host ("Callsign is: {0}" -f $CALLSIGN) -ForegroundColor Black -BackgroundColor Yellow
Publish-Module -Name $CALLSIGN -NuGetApiKey $env:NuGetToken -Verbose -Force Publish-Module -Name $CALLSIGN -NuGetApiKey $env:NuGetToken -Verbose -Force
} }
Else { Else {
Write-Host "Skip publishing to PS Gallery because we are on $($env:APPVEYOR_REPO_BRANCH) branch." -ForegroundColor Yellow Write-Host "Skip publishing to PS Gallery because we are on $($env:APPVEYOR_REPO_BRANCH) branch." -ForegroundColor Black -BackgroundColor Yellow
# had to remove the publish-Module statement because it would publish although the -WhatIf is given. # had to remove the publish-Module statement because it would publish although the -WhatIf is given.
# Publish-Module -Name $CALLSIGN -NuGetApiKey $env:NuGetToken -Verbose -WhatIf # Publish-Module -Name $CALLSIGN -NuGetApiKey $env:NuGetToken -Verbose -WhatIf
} }
} }
Catch { Catch {
$MsgParams = @{ $MsgParams = @{
Message = 'Could not deploy module to PSGallery.' Message = 'Could not deploy module to PSGallery.'
Category = 'Error' Category = 'Error'
Details = $_.Exception.Message Details = $_.Exception.Message
} }
Add-AppveyorMessage @MsgParams Add-AppveyorMessage @MsgParams
Throw $MsgParams.Message Throw $MsgParams.Message

178
tools/CoverallsIO.psm1 Normal file
View File

@ -0,0 +1,178 @@
function Get-GitInfo {
[CmdletBinding()]
param(
[string]$BranchName
)
if ($Env:AppVeyor) {
return [PSCustomObject]@{
head = [PSCustomObject]@{
id = $Env:APPVEYOR_REPO_COMMIT
author_name = $Env:APPVEYOR_REPO_COMMIT_AUTHOR
author_email = $Env:APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL
comitter_name = $Env:APPVEYOR_REPO_COMMIT_AUTHOR
comitter_email = $Env:APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL
message = $Env:APPVEYOR_REPO_COMMIT_MESSAGE
}
branch = $Env:APPVEYOR_REPO_BRANCH
}
}
else {
if (-not $BranchName) {
$BranchName = (git rev-parse --abbrev-ref HEAD)
}
return [PSCustomObject]@{
head = [PSCustomObject]@{
id = (git log --format="%H" HEAD -1)
author_name = (git log --format="%an" HEAD -1)
author_email = (git log --format="%ae" HEAD -1)
committer_name = (git log --format="%cn" HEAD -1)
committer_email = (git log --format="%ce" HEAD -1)
message = (git log --format="%s" HEAD -1)
}
branch = $BranchName
}
}
}
function New-CoverageReport {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[PSCustomObject]$CodeCoverage,
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[string]$RepoToken,
[Parameter(Mandatory = $false)]
[ValidateNotNullOrEmpty()]
[string]$ModuleRoot = $(Get-Location)
)
begin {
$CoverReport = [PSCustomObject]@{
repo_token = $RepoToken
commit_sha = (git log --format="%H" HEAD -1)
git = Get-GitInfo
service_name = 'appveyor'
source_files = @()
}
}
process {
# Find all files with hit commands -> These file have pester tests
$UsedFiles = $CodeCoverage.AnalyzedFiles | Where-Object {
$CodeCoverage.HitCommands.File -contains $_
}
foreach ($SourceFile in $UsedFiles) {
$Lines = (Get-Content -Path $SourceFile | Measure-Object).Count
Write-Verbose ("SourceFile: {0} | LinesCount: {1}" -f $SourceFile, $Lines)
$CoverageArray = @()
$Hits = 0
$Missed = 0
for ($LinePointer = 1; $LinePointer -le $Lines; $LinePointer++) {
# Get only hit commands from current src file
$curHits = $CodeCoverage.HitCommands | Where-Object {
$_.File -eq $SourceFile
}
[int]$Hits = (
$curHits | Where-Object {
$_.Line -eq $LinePointer
} | Measure-Object
).Count
# again filter only missed commands from the curent file
$curMissed = $CodeCoverage.MissedCommands | Where-Object {
$_.File -eq $SourceFile
}
[int]$Missed = (
$curMissed | Where-Object {
$_.Line -eq $LinePointer
} | Measure-Object
).Count
Write-Verbose ("SourceFile:{0} | Line: {1} | Hits: {2} | Missed: {3}" -f $SourceFile, $LinePointer, $Hits, $Missed)
if ((-not $Hits -gt 0) -and (-not $Missed -gt 0)) {
$CoverageArray += 'null'
}
else {
if ($Hits -gt 0) {
$CoverageArray += $Hits
}
elseif ($Missed -gt 0) {
$CoverageArray += 0
}
}
}
# Get rid of the quotation
$CoverageArray = $CoverageArray -Replace '"', ''
$CoverageSourceFile = [PSCustomObject]@{
name = $SourceFile.Replace($ModuleRoot, '').Replace('\', '/')
source_digest = (Get-FileHash -Path $SourceFile -Algorithm MD5).Hash
coverage = $CoverageArray
}
If ($CoverageSourceFile.Name.StartsWith('/')) {
$CoverageSourceFile.Name = $CoverageSourceFile.Name.Remove(0, 1)
}
$CoverReport.source_files += $CoverageSourceFile
}
# Find all untested files to create a null coverage file
$UnUsedFiles = $CodeCoverage.AnalyzedFiles | Where-Object {
$CodeCoverage.HitCommands.File -notcontains $_
}
foreach ($UnUsedFile in $UnUsedFiles) {
$Lines = (Get-Content -Path $UnUsedFile | Measure-Object).Count
$CoverageArray = @()
for ($LinePointer = 1; $LinePointer -le $Lines; $LinePointer++) {
$CoverageArray += '0'
}
$CoverageSourceFile = [PSCustomObject]@{
name = $UnUsedFile.Replace($ModuleRoot, '').Replace('\', '/')
source_digest = (Get-FileHash -Path $UnUsedFile -Algorithm MD5).Hash
coverage = $CoverageArray
}
if ($CoverageSourceFile.Name.StartsWith('/')) {
$CoverageSourceFile.Name = $CoverageSourceFile.Name.Remove(0, 1)
}
$CoverReport.source_files += $CoverageSourceFile
}
}
end {
Write-Output $CoverReport
}
}
function Publish-CoverageReport () {
[CmdletBinding()]
param(
[Parameter(Mandatory = $True)]
[ValidateNotNullOrEmpty()]
[PSCustomObject]$CoverageReport
)
begin {
Add-Type -AssemblyName System.Net.Http
}
process {
$CoverageJSON = ConvertTo-Json $CoverageReport -Depth 5
# Try to fix null elements in coverage array.
$CoverageJSON = $CoverageJSON.Replace('"null"', 'null')
$stringContent = New-Object System.Net.Http.StringContent ($CoverageJSON)
$httpClient = New-Object System.Net.Http.Httpclient
$formdata = New-Object System.Net.Http.MultipartFormDataContent
$formData.Add($stringContent, "json_file", "coverage.json")
$result = $httpClient.PostAsync('https://coveralls.io/api/v1/jobs', $formData).Result
$content = $result.Content.ReadAsStringAsync()
}
end {
Write-Output $Content
}
}

66
tools/Travis.psm1 Normal file
View File

@ -0,0 +1,66 @@
$Global:ProgressPreference = 'SilentlyContinue'
$CALLSIGN = 'PSCredentialStore'
Write-Host ("Callsign is: {0}" -f $CALLSIGN) -ForegroundColor Black -BackgroundColor Yellow
function Invoke-InstallDependencies {
[CmdletBinding()]
Param()
process {
try {
Write-Host '===== Environment Vars: =====' -ForegroundColor Black -BackgroundColor Yellow
Get-ChildItem -Path Env:
Write-Host -Message '===== Existing Variables: =====' -ForegroundColor Black -BackgroundColor Yellow
Get-Variable -Name * | Format-Table -AutoSize
Get-PackageProvider -ListAvailable
Import-PackageProvider -Name 'NuGet' -MinimumVersion '2.8.5.208' -Verbose -Force
Install-Module -Name 'Pester' -Scope CurrentUser -RequiredVersion '4.4.2' -Force -SkipPublisherCheck -AllowClobber -Verbose
Install-Module -Name 'posh-git' -Scope CurrentUser -RequiredVersion '1.0.0-beta2' -Force -SkipPublisherCheck -AllowClobber -AllowPrerelease -Verbose
Import-Module -Name 'Pester', 'posh-git' -Verbose
}
catch {
$_.Exception.Message | Write-Error
throw 'Could not install the required dependencies!'
}
}
}
function Invoke-UnitTests {
[CmdletBinding()]
Param()
process {
try {
Write-Host '===== Preload internal private functions =====' -ForegroundColor Black -BackgroundColor Yellow
$Privates = Get-ChildItem -Path (Join-Path -Path $Env:TRAVIS_BUILD_DIR -ChildPath '/src/Private/*') -Include "*.ps1" -Recurse
foreach ($File in $Privates) {
if (Test-Path -Path $File.FullName) {
. $File.FullName
Write-Verbose -Message ('Private function dot-sourced: {0}' -f $File.FullName) -Verbose
}
else {
Write-Warning -Message ('Could not find file: {0} !' -f $File.FullName)
}
}
}
catch {
$_.Exception.Message | Write-Error
throw 'Could not load required private functions!'
}
Write-Host '===== Running Pester =====' -ForegroundColor Black -BackgroundColor Yellow
$TestFiles = Get-ChildItem -Path (Join-Path -Path '.' -ChildPath './tests/*.Tests.ps1') -Recurse| Sort-Object -Property Name
$TestResults = Invoke-Pester -Script $TestFiles -ExcludeTag 'Disabled' -PassThru
if ($TestResults.FailedCount -gt 0) {
throw ('{0} tests failed!' -f $TestResults.FailedCount)
}
}
}

14
tools/travis.sh Normal file
View File

@ -0,0 +1,14 @@
set -x
ulimit -n 4096
echo "TRAVIS_EVENT_TYPE value $TRAVIS_EVENT_TYPE"
if [ $TRAVIS_EVENT_TYPE = cron ] || [ $TRAVIS_EVENT_TYPE = api ]; then
sudo pwsh -NoProfile -NonInteractive -c "Import-Module ./tools/Travis.psm1;
Invoke-InstallDependencies;
Invoke-UnitTests;"
else
sudo pwsh -NoProfile -NonInteractive -c "Import-Module ./tools/Travis.psm1;
Invoke-InstallDependencies;
Invoke-UnitTests;"
fi