From 9ed4091402844859607b48c23e8057788e224f5e Mon Sep 17 00:00:00 2001 From: Shawn Melton <11204251+wsmelton@users.noreply.github.com> Date: Fri, 19 Mar 2021 00:05:34 -0500 Subject: [PATCH] Set-TssSecretField - closes #117 --- src/functions/secrets/Set-SecretField.ps1 | 145 +++++++++++++++++++++ tests/secrets/Set-TssSecretField.Tests.ps1 | 52 ++++++++ 2 files changed, 197 insertions(+) create mode 100644 src/functions/secrets/Set-SecretField.ps1 create mode 100644 tests/secrets/Set-TssSecretField.Tests.ps1 diff --git a/src/functions/secrets/Set-SecretField.ps1 b/src/functions/secrets/Set-SecretField.ps1 new file mode 100644 index 00000000..9634da90 --- /dev/null +++ b/src/functions/secrets/Set-SecretField.ps1 @@ -0,0 +1,145 @@ +function Set-SecretField { + <# + .SYNOPSIS + Set value for a Secret Field + + .DESCRIPTION + Set value for a Secret Field + + .EXAMPLE + $session = New-TssSession -SecretServer https://alpha -Credential $ssCred + Set-TssSecretField -TssSession $session Id 42 -Slug notes -Value "Test test test" + + Set Notes field on Secret 42 to the value "Test test test" + + .LINK + https://thycotic-ps.github.io/thycotic.secretserver/commands/Set-TssSecretField + + .NOTES + Requires TssSession object returned by New-TssSession + #> + [cmdletbinding(SupportsShouldProcess, DefaultParameterSetName = 'all')] + param( + # TssSession object created by New-TssSession for auth + [Parameter(Mandatory,ValueFromPipeline,Position = 0)] + [TssSession] + $TssSession, + + # Folder Id to modify + [Parameter(Mandatory,ValueFromPipelineByPropertyName)] + [Alias("SecretId")] + [int[]] + $Id, + + # Field name (slug) of the secret + [Parameter(Mandatory)] + [Alias('Field')] + [string] + $Slug, + + # Value to set for the provided field + [string] + $Value, + + # Clear/blank out the field value + [switch] + $Clear, + + # Path of file to attach + [ValidateScript({ + if (Test-Path $_ -PathType Container) { + throw "Path [$_] is a directory, provide full file path" + } else { + $true + } + })] + $Path, + + # Comment to provide for restricted secret (Require Comment is enabled) + [Parameter(ParameterSetName = 'restricted')] + [string] + $Comment, + + # Force check-in of the Secret + [Parameter(ParameterSetName = 'restricted')] + [switch] + $ForceCheckIn, + + # Associated Ticket Number + [Parameter(ParameterSetName = 'restricted')] + [int] + $TicketNumber, + + #Associated Ticket System ID + [Parameter(ParameterSetName = 'restricted')] + [int] + $TicketSystemId + ) + begin { + $setParams = $PSBoundParameters + $invokeParams = . $GetInvokeTssParams $TssSession + } + process { + Write-Verbose "Provided command parameters: $(. $GetInvocation $PSCmdlet.MyInvocation)" + if ($setParams.ContainsKey('TssSession') -and $TssSession.IsValidSession()) { + . $CheckVersion $TssSession '10.9.0000' $PSCmdlet.MyInvocation + foreach ($secret in $Id) { + if ($setParams.ContainsKey('Clear') -and $setParams.ContainsKey('Value')) { + Write-Warning "Clear and Value provided, only one is supported" + return + } + + $fieldBody = @{} + if ($setParams.ContainsKey('Clear')) { + $fieldBody.Add('value',"") + } + if ($setParams.ContainsKey('Value')) { + $fieldBody.Add('value',$Value) + } + + if ($setParams.ContainsKey('Path')) { + $fileName = Split-Path $Path -Leaf + $fieldBody.Add('fileName',$fileName) + + $fileBinary = [IO.File]::ReadAllBytes($Path) + $fieldBody.Add('fileAttachment',$fileBinary) + } + + if ($restrictedParams.Count -gt 0) { + switch ($restrictedParams) { + 'Comment' { $fieldBody.Add('comment', $Comment) } + 'ForceCheckIn' { $fieldBody.Add('forceCheckIn', [boolean]$ForceCheckIn) } + 'TicketNumber' { $fieldBody.Add('ticketNumber', $TicketNumber) } + 'TicketSystemId' { $fieldBody.Add('ticketSystemId', $TicketSystemId) } + } + } + + $uri = $TssSession.ApiUrl, 'secrets', $secret, 'fields', $Slug -join '/' + $invokeParams.Uri = $uri + $invokeParams.Method = 'PUT' + $invokeParams.Body = $fieldBody | ConvertTo-Json -Depth 5 + + if ($PSCmdlet.ShouldProcess("SecretId: $secret", "$($invokeParams.Method) $uri with:`n$($invokeParams.Body)`n")) { + Write-Verbose "$($invokeParams.Method) $uri with:`n$($invokeParams.Body)`n" + try { + $fieldResponse = Invoke-TssRestApi @invokeParams + } catch { + Write-Warning "Issue setting field $Field on secret [$secret]" + $err = $_ + . $ErrorHandling $err + } + + if ($fieldResponse -eq $Value) { + Write-Verbose "Secret [$secret] field $Field updated successfully" + } elseif ($setParams.ContainsKey('Clear') -and ($null -eq $fieldResponse)) { + Write-Verbose "Secret [$secret] field $Field cleared successfully" + } else { + Write-Verbose "Response for secret [$secret]: $fieldResponse" + } + } + } + } else { + Write-Warning "No valid session found" + } + } +} \ No newline at end of file diff --git a/tests/secrets/Set-TssSecretField.Tests.ps1 b/tests/secrets/Set-TssSecretField.Tests.ps1 new file mode 100644 index 00000000..3b460c20 --- /dev/null +++ b/tests/secrets/Set-TssSecretField.Tests.ps1 @@ -0,0 +1,52 @@ +BeforeDiscovery { + $commandName = Split-Path ($PSCommandPath.Replace('.Tests.ps1','')) -Leaf + . ([IO.Path]::Combine([string]$PSScriptRoot, '..', 'constants.ps1')) +} +Describe "$commandName verify parameters" { + BeforeDiscovery { + [object[]]$knownParameters = 'TssSession','Id', 'Slug', 'Value', 'Clear', 'Path', 'Comment', 'ForceCheckIn', 'TicketNumber', 'TicketSystemId' + [object[]]$currentParams = ([Management.Automation.CommandMetaData]$ExecutionContext.SessionState.InvokeCommand.GetCommand($commandName,'Function')).Parameters.Keys + [object[]]$commandDetails = [System.Management.Automation.CommandInfo]$ExecutionContext.SessionState.InvokeCommand.GetCommand($commandName,'Function') + $unknownParameters = Compare-Object -ReferenceObject $knownParameters -DifferenceObject $currentParams -PassThru + } + Context "Verify parameters" -Foreach @{currentParams = $currentParams } { + It "$commandName should contain <_> parameter" -TestCases $knownParameters { + $_ -in $currentParams | Should -Be $true + } + It "$commandName should not contain parameter: <_>" -TestCases $unknownParameters { + $_ | Should -BeNullOrEmpty + } + } +} +Describe "$commandName functions" { + Context "Checking" { + BeforeAll { + $session = [pscustomobject]@{ + ApiVersion = 'api/v1' + Take = 2147483647 + SecretServer = 'http://alpha/' + ApiUrl = 'http://alpha/api/v1' + AccessToken = 'AgJf5YLFWtzw2UcBrM1s1KB2BGZ5Ufc4qLZ' + RefreshToken = '9oacYFZZ0YqgBNg0L7VNIF6-Z9ITE51Qplj' + TokenType = 'bearer' + ExpiresIn = 1199 + } + Mock -Verifiable -CommandName Get-TssVersion -MockWith { + return @{ + Version = '10.9.000033' + } + } + + $secretId = Get-Random -Maximum 12 + $notes = (New-Guid).Guid + Mock -Verifiable -CommandName Invoke-TssRestApi -ParameterFilter { $Uri -eq "$($session.ApiUrl)/secrets/$secretId/fields/notes" } -MockWith { + return $notes + } + $object = Set-TssSecretField -TssSession $session -Id $secretId -Slug notes -Value $notes + Assert-VerifiableMock + } + It "Should be empty" { + $object | Should -BeNullOrEmpty + } + } +} \ No newline at end of file