From 940e2ca5264a91767f7db445ba5cfe85db26a2e0 Mon Sep 17 00:00:00 2001 From: Chris Hunt <chunt@stackoverflow.com> Date: Fri, 16 Sep 2022 10:48:22 -0400 Subject: [PATCH 1/2] Get, List, Create Password is being set to "System.Security.SecureString" --- op.crescendo.config.json | 96 ++++++ src/class/CommandBuilder.ps1 | 492 --------------------------- src/class/OpCommand.ps1 | 0 src/class/en-US/message.psd1 | 6 - src/op-powershell.psd1 | 139 ++++++++ src/op-powershell.psm1 | 352 ++++++++++++++++++- tests/class_CommandBuilder.Tests.ps1 | 251 -------------- tests/class_OpCommand.Tests.ps1 | 267 --------------- 8 files changed, 578 insertions(+), 1025 deletions(-) create mode 100644 op.crescendo.config.json delete mode 100644 src/class/CommandBuilder.ps1 delete mode 100644 src/class/OpCommand.ps1 delete mode 100644 src/class/en-US/message.psd1 create mode 100644 src/op-powershell.psd1 delete mode 100644 tests/class_CommandBuilder.Tests.ps1 delete mode 100644 tests/class_OpCommand.Tests.ps1 diff --git a/op.crescendo.config.json b/op.crescendo.config.json new file mode 100644 index 0000000..cb81451 --- /dev/null +++ b/op.crescendo.config.json @@ -0,0 +1,96 @@ +{ + "$schema": "https://raw.githubusercontent.com/PowerShell/Crescendo/master/Microsoft.PowerShell.Crescendo/schemas/2022-06", + "Commands": [ + { + "Verb": "Get", + "Noun": "OpSecretList", + "OriginalName": "op", + "OriginalCommandElements": [ + "item", + "list", + "--format", + "json" + ], + "Description": "List secrets", + "OutputHandlers": [ + { + "ParameterSetName": "Default", + "Handler": "$input | ConvertFrom-Json", + "StreamOutput": true + } + ] + }, + { + "Verb": "Get", + "Noun": "OpSecret", + "OriginalName": "op", + "OriginalCommandElements": [ + "item", + "get", + "--format", + "json" + ], + "Description": "Get a secret", + "Parameters": [ + { + "Name": "Name", + "OriginalName": "", + "Position": 0, + "Description": "An item's name or its unique identifier", + "ParameterType": "string" + } + ], + "OutputHandlers": [ + { + "ParameterSetName": "Default", + "Handler": "$input | ConvertFrom-Json", + "StreamOutput": true + } + ] + }, + { + "Verb": "New", + "Noun": "OpSecret", + "OriginalName": "op", + "OriginalCommandElements": [ + "item", + "create" + ], + "Description": "Create a new secret", + "Parameters": [ + { + "Name": "Category", + "OriginalName": "--category=", + "ParameterType": "string", + "Description": "Specify which template to use", + "DefaultValue": "login", + "AdditionalParameterAttributes": [ + "[ValidateSet('login','password')]" + ], + "NoGap": true + }, + { + "Name": "Name", + "OriginalName": "--title=", + "ParameterType": "string", + "NoGap": true + }, + { + "Name": "Username", + "OriginalName": "--username=", + "ParameterType": "string", + "NoGap": true + }, + { + "Name": "Password", + "OriginalName": "password=", + "ParameterType": "SecureString", + "Description": "The password", + "ArgumentTransform": "param([SecureString]$p) ConvertFrom-SecureString -SecureString $p -AsPlainText", + "Mandatory": true, + "NoGap": true + } + ] + } + ] +} \ No newline at end of file diff --git a/src/class/CommandBuilder.ps1 b/src/class/CommandBuilder.ps1 deleted file mode 100644 index 8f39f98..0000000 --- a/src/class/CommandBuilder.ps1 +++ /dev/null @@ -1,492 +0,0 @@ -class CommandArgument { - [string] $Name - [bool] $HasValue = $false - [bool] $IsSensitive = $false - [object[]] $Value - hidden [string] $Seperator = '' - hidden [scriptblock] $ValueReduce = { $args -join ',' } - hidden [bool] $HasQuote = $false - - CommandArgument([string]$name) { - $this.Name = $name - } - - CommandArgument([string]$name, [object[]]$value) { - $this.Name = $name - $this.Value = $value - $this.Seperator = ' ' - $this.HasValue = $true - } - - CommandArgument([string]$name, [object[]]$value, [string]$seperator) { - $this.Name = $name - $this.Value = $value - $this.HasValue = $true - $this.Seperator = $seperator - } - - CommandArgument([string]$name, [object[]]$value, [string]$seperator, [scriptblock]$reduce) { - $this.Name = $name - $this.Value = $value - $this.HasValue = $true - $this.Seperator = $seperator - $this.ValueReduce = $reduce - } - - [CommandArgument] AddValue($v) { - $this.Value += $v - $this.HasValue = $true - - if ([string]::IsNullOrEmpty($this.Seperator)) { - $this.Seperator = ' ' - } - - return $this - } - - [CommandArgument] SetSeparator([string]$string) { - $this.Seperator = $string - - return $this - } - - [CommandArgument] SetQuotedValue() { - $this.HasQuote = $true - - return $this - } - - [CommandArgument] SetValueReduce([scriptblock]$script) { - if ($script.ToString() -notlike '*$args *') { - throw 'ScriptBlock must contain "$args" variable.' - } - - $this.ValueReduce = $script - - return $this - } - - [string] ToString() { - return $this.ToString($false) - } - - [string] ToString([bool]$sanitize) { - $_argument = $this.Name - $_value = [string]::Empty - $_combined = [string]::Empty - - if ($this.HasValue) { - $_value = if ($this.Value.Count -gt 1) { $this.ValueReduce.Invoke($this.Value) } else { $this.Value } - - if ($this.IsSensitive -and $sanitize) { - $_value = '*****' - } - - if ($this.HasQuote) { - $_value = '"{0}"' -f $_value - } - } - - $_combined = $_argument, $_value -join $this.Seperator - return $_combined - } - - [System.Collections.Generic.List[CommandArgument]] AsMultipleArguments() { - $_argArray = [System.Collections.Generic.List[CommandArgument]]::new() - if ($this.HasValue) { - foreach ($_value in $this.Value) { - $_newArg = [CommandArgument]::new($this.Name, $_value) - $_newArg.Seperator = $this.Seperator - $_newArg.ValueReduce = $this.ValueReduce - $_newArg.IsSensitive = $this.IsSensitive - $_argArray.Add($_newArg) - } - } - - return $_argArray - } -} - -class CommandRunResult { - [bool] $Success - [string] $Output - hidden [string] $StdOut - hidden [string] $StdErr - - CommandRunResult() { - $this.Success = $false - } -} - -class CommandBuilder { - [string] $Name - hidden [System.Collections.Generic.List[CommandArgument]] $ArgumentList = [System.Collections.Generic.List[CommandArgument]]::new() - - CommandBuilder($name) { - $this.Name = $name - - } - - [CommandBuilder] AddArgument([CommandArgument]$arg) { - $this.ArgumentList.Add($arg) - - return $this - } - - [CommandBuilder] AddArgument([string]$name) { - $this.ArgumentList.Add([CommandArgument]::new($name)) - - return $this - } - - [CommandBuilder] AddArgument([string]$name, [object]$value) { - $this.ArgumentList.Add([CommandArgument]::new($name, $value)) - - return $this - } - - [CommandBuilder] AddArgument([string]$name, [object]$value, [string]$seperator) { - $this.ArgumentList.Add([CommandArgument]::new($name, $value, $seperator)) - - return $this - } - - [string] ToString() { - return $this.ToString($false) - } - - [string] ToString([bool]$sanitize) { - $_command = $this.Name - - if ($this.ArgumentList.Count -ge 1) { - $_argument = $this.ArgumentList | ForEach-Object { - $_.ToString($sanitize) - } - return '{0} {1}' -f $_command, ($_argument -join ' ') - } - else { - return '{0}' -f $_command - } - } - - [Diagnostics.ProcessStartInfo] GetProcessStartInfo() { - $_processInfo = [Diagnostics.ProcessStartInfo]::new() - $_processInfo.FileName = $this.Name - $_processInfo.RedirectStandardError = $true - $_processInfo.RedirectStandardOutput = $true - $_processInfo.RedirectStandardInput = $false - $_processInfo.UseShellExecute = $false - - $this.ArgumentList | ForEach-Object { - if ($_.HasValue) { - $_value = $_.ValueReduce.Invoke($_.Value) - $_processInfo.ArgumentList.Add($_.Name) - $_processInfo.ArgumentList.Add($_value) - } - else { - $_processInfo.ArgumentList.Add($_.Name) - } - } - - return $_processInfo - } - - [string] ParseStdErr([string]$message) { - return $message - } - - [CommandRunResult] Run() { - Write-Verbose ('(Run) Command="{0}"' -f $this.ToString($true)) - - $_process = [Diagnostics.Process]::new() - $_process.StartInfo = $this.GetProcessStartInfo() - $_cleanExit = $false - $_message = [string]::Empty - $_result = [CommandRunResult]::new() - - try { - $_process.Start() | Out-Null - } - catch [ObjectDisposedException] { - Write-Error 'No file name was specified.' - } - catch [InvalidOperationException] { - Write-Error 'The process object has already been disposed.' - } - catch [PlatformNotSupportedException] { - Write-Error 'This member is not supported on this platform.' - } - catch { - Write-Error 'An error occurred when opening the associated file.' - } - - try { - $_process.WaitForExit(10000) - $_cleanExit = $true - } - catch [SystemException] { - Write-Error 'No process Id has been set, and a Handle from which the Id property can be determined does not exist or there is no process associated with this Process object.' - } - catch { - Write-Error 'The wait setting could not be accessed.' - } - - if ($_cleanExit) { - - $_stdOut = $_process.StandardOutput.ReadToEnd() - $_message = $_stdOut - $_stdErr = $_process.StandardError.ReadToEnd() - - if ([string]::IsNullOrEmpty($_stdErr) ) { - $_result.Success = $true - } - else { - $_message = $this.ParseStdErr($_stdErr) - } - - $_result.Output = $_message - $_result.StdOut = $_stdOut - $_result.StdErr = $_stdErr - } - - return $_result - } -} - -class OpCommandRunResult : CommandRunResult { - [bool] $SigninRequired - - OpCommandRunResult() : base() { - $this.SigninRequire = $false - } -} - -class OpCommand : CommandBuilder { - hidden $LocalizedMessage - - OpCommand() : base('op') { - $this.LocalizedMessage = Import-LocalizedData -FileName message.psd1 -BaseDirectory $PSScriptRoot -ErrorAction Stop - } - - [string] ParseStdErr([string]$message) { - $_patternSignIn = [regex]'\[ERROR\] (?<date>\d{4}\W\d{1,2}\W\d{1,2}) (?<time>\d{2}:\d{2}:\d{2}).+(?<message>sign(ed){0,1} in)' - - if ($_patternSignIn.Match($message).Success) { - return 'errorSignin' - } - - return [string]::Empty - } - - [OpCommandRunResult] Run() { - Write-Verbose ('(Run) Command="{0}"' -f $this.ToString($true)) - - $_process = [Diagnostics.Process]::new() - $_process.StartInfo = $this.GetProcessStartInfo() - $_cleanExit = $false - $_message = [string]::Empty - $_result = [OpCommandRunResult]::new() - - try { - $_process.Start() | Out-Null - } - catch [ObjectDisposedException] { - Write-Error 'No file name was specified.' - } - catch [InvalidOperationException] { - Write-Error 'The process object has already been disposed.' - } - catch [PlatformNotSupportedException] { - Write-Error 'This member is not supported on this platform.' - } - catch { - Write-Error 'An error occurred when opening the associated file.' - } - - try { - $_process.WaitForExit(10000) - $_cleanExit = $true - } - catch [SystemException] { - Write-Error 'No process Id has been set, and a Handle from which the Id property can be determined does not exist or there is no process associated with this Process object.' - } - catch { - Write-Error 'The wait setting could not be accessed.' - } - - if ($_cleanExit) { - - $_stdOut = $_process.StandardOutput.ReadToEnd() - $_message = $_stdOut - $_stdErr = $_process.StandardError.ReadToEnd() - - if ([string]::IsNullOrEmpty($_stdErr) ) { - $_result.Success = $true - } - else { - $_parsedErrorMessage = $this.ParseStdErr($_stdErr) - - if ($_parsedErrorMessage -eq 'errorSignin') { - $_result.SigninRequired = $true - $_message = $this.LocalizedMessage[$_parsedErrorMessage] - } - } - - $_result.Output = $_message - $_result.StdOut = $_stdOut - $_result.StdErr = $_stdErr - } - - return $_result - } -} - -class OpCommandList : OpCommand { - - OpCommandList() : base() { - $this.AddArgument('list') - } -} - -class OpCommandGet : OpCommand { - - OpCommandGet() : base() { - $this.AddArgument('get') - } -} - -class OpCommandListItem : OpCommandList { - - OpCommandListItem() : base() { - $this.AddArgument('items') - } - - [OpCommandListItem] WithVault([string]$name) { - $this.AddArgument('--vault', $name) - return $this - } - - [OpCommandListItem] WithCategory([string[]]$category) { - $this.AddArgument('--categories', $category) - return $this - } - - [OpCommandListItem] WithCategory([string]$c1, [string]$c2) { - $this.AddArgument('--categories', @($c1, $c2)) - return $this - } - - [OpCommandListItem] WithCategory([string]$c1, [string]$c2, [string]$c3) { - $this.AddArgument('--categories', @($c1, $c2, $c3)) - return $this - } - - [OpCommandListItem] WithTag([string[]]$tag) { - $this.AddArgument('--tags', $tag) - return $this - } - - [OpCommandListItem] WithTag([string]$t1, [string]$t2) { - $this.AddArgument('--tags', @($t1, $t2)) - return $this - } - - [OpCommandListItem] WithTag([string]$t1, [string]$t2, [string]$t3) { - $this.AddArgument('--tags', @($t1, $t2, $t3)) - return $this - } -} - -class OpCommandGetItem : OpCommandGet { - hidden [bool] $hasField = $false - hidden [bool] $hasFormat = $false - - OpCommandGetItem([string]$item) : base() { - $this.AddArgument('item', $item) - } - - [OpCommandGetItem] WithVault([string]$name) { - $this.AddArgument('--vault', $name) - return $this - } - - [OpCommandGetItem] WithField([string[]]$field) { - $this.AddArgument('--fields', $field) - $this.hasField = $true - return $this - } - - [OpCommandGetItem] WithField([string]$f1, [string]$f2) { - $this.AddArgument('--fields', @($f1, $f2)) - $this.hasField = $true - return $this - } - - [OpCommandGetItem] WithField([string]$f1, [string]$f2, [string]$f3) { - $this.AddArgument('--fields', @($f1, $f2, $f3)) - $this.hasField = $true - return $this - } - - hidden [OpCommandGetItem] WithFormat([string]$format) { - if ($this.hasFormat) { - Write-Error -Message 'Format has already been set' - return $this - } - - if (-not $this.hasField) { - Write-Error -Message 'Format can only be used with Fields' - return $this - } - - $this.AddArgument('--format', $format) - $this.hasFormat = $true - - return $this - } - - [OpCommandGetItem] WithJsonFormat() { - return $this.WithFormat('JSON') - } - - [OpCommandGetItem] WithCsvFormat() { - return $this.WithFormat('Csv') - } - - [OpCommandGetItem] IncludeTrash() { - $this.AddArgument('--include-trash') - return $this - } -} - -class OpCommandSignin : OpCommand { - hidden [securestring]$Password - - OpCommandSignin([string]$account, [securestring]$password) : base() { - $this.AddArgument('signin') - $this.AddArgument($account) - $this.AddArgument('--raw') - $this.Password = $password - } - - [OpCommandRunResult] Run() { - Write-Verbose ('(Run) Command="{0}"' -f $this.ToString($true)) - - $_result = [OpCommandRunResult]::new() - - $_expression = $this.ToString() - $_output = $this.LocalizedMessage['errorNoOutput'] - - try { - $_output = Invoke-Expression -Command $_expression -ErrorAction Stop 2>&1 - } - catch { - Write-Error -Message $_.Message - } - - $_result.Output = $_output - $_result.Success = $true - - return $_result - } -} \ No newline at end of file diff --git a/src/class/OpCommand.ps1 b/src/class/OpCommand.ps1 deleted file mode 100644 index e69de29..0000000 diff --git a/src/class/en-US/message.psd1 b/src/class/en-US/message.psd1 deleted file mode 100644 index cd0a72b..0000000 --- a/src/class/en-US/message.psd1 +++ /dev/null @@ -1,6 +0,0 @@ - -ConvertFrom-StringData -StringData @' -errorSignin = You are not currently signed in. -errorNoOutput = The command returned no output. -'@ - diff --git a/src/op-powershell.psd1 b/src/op-powershell.psd1 new file mode 100644 index 0000000..19a4aa3 --- /dev/null +++ b/src/op-powershell.psd1 @@ -0,0 +1,139 @@ +# +# Module manifest for module 'op-powershell' +# +# Generated by: chunt +# +# Generated on: 9/16/2022 +# + +@{ + +# Script module or binary module file associated with this manifest. +RootModule = 'op-powershell.psm1' + +# Version number of this module. +ModuleVersion = '0.0.1' + +# Supported PSEditions +# CompatiblePSEditions = @() + +# ID used to uniquely identify this module +GUID = '6151db2d-c545-4c9a-8f90-65822264bddc' + +# Author of this module +Author = 'chunt' + +# Company or vendor of this module +CompanyName = 'Unknown' + +# Copyright statement for this module +Copyright = '(c) chunt. All rights reserved.' + +# Description of the functionality provided by this module +# Description = '' + +# Minimum version of the PowerShell engine required by this module +PowerShellVersion = '5.1.0' + +# Name of the PowerShell host required by this module +# PowerShellHostName = '' + +# Minimum version of the PowerShell host required by this module +# PowerShellHostVersion = '' + +# Minimum version of Microsoft .NET Framework required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# DotNetFrameworkVersion = '' + +# Minimum version of the common language runtime (CLR) required by this module. This prerequisite is valid for the PowerShell Desktop edition only. +# ClrVersion = '' + +# Processor architecture (None, X86, Amd64) required by this module +# ProcessorArchitecture = '' + +# Modules that must be imported into the global environment prior to importing this module +# RequiredModules = @() + +# Assemblies that must be loaded prior to importing this module +# RequiredAssemblies = @() + +# Script files (.ps1) that are run in the caller's environment prior to importing this module. +# ScriptsToProcess = @() + +# Type files (.ps1xml) to be loaded when importing this module +# TypesToProcess = @() + +# Format files (.ps1xml) to be loaded when importing this module +# FormatsToProcess = @() + +# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess +# 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. +FunctionsToExport = 'Get-OpSecretList', 'Get-OpSecret', 'New-OpSecret' + +# 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 = @() + +# Variables to export from this module +# 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. +AliasesToExport = @() + +# DSC resources to export from this module +# DscResourcesToExport = @() + +# List of all modules packaged with this module +# ModuleList = @() + +# List of all files packaged with this module +# 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. +PrivateData = @{ + + PSData = @{ + + # Tags applied to this module. These help with module discovery in online galleries. + Tags = 'CrescendoBuilt' + + # A URL to the license for this module. + # LicenseUri = '' + + # A URL to the main website for this project. + # ProjectUri = '' + + # A URL to an icon representing this module. + # IconUri = '' + + # ReleaseNotes of this module + # ReleaseNotes = '' + + # Prerelease string of this module + # Prerelease = '' + + # Flag to indicate whether the module requires explicit user acceptance for install/update/save + # RequireLicenseAcceptance = $false + + # External dependent modules of this module + # ExternalModuleDependencies = @() + + } # End of PSData hashtable + + + # CrescendoVersion + CrescendoVersion = '1.0.0' + + # CrescendoGenerated + CrescendoGenerated = '09/16/2022 10:46:23' + +} # End of PrivateData hashtable + +# HelpInfo URI of this module +# HelpInfoURI = '' + +# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. +# DefaultCommandPrefix = '' + +} + diff --git a/src/op-powershell.psm1 b/src/op-powershell.psm1 index 3fb9eb6..bfc8523 100644 --- a/src/op-powershell.psm1 +++ b/src/op-powershell.psm1 @@ -1,11 +1,345 @@ -$classFolder = Join-Path -Path $PSScriptRoot -ChildPath class -$publicFolder = Join-Path -Path $PSScriptRoot -ChildPath public -$CommandBuilderFile = Join-Path -Path $classFolder -ChildPath CommandBuilder.ps1 -$OpCommandFile = Join-Path -Path $classFolder -ChildPath OpCommand.ps1 +# Module created by Microsoft.PowerShell.Crescendo +class PowerShellCustomFunctionAttribute : System.Attribute { + [bool]$RequiresElevation + [string]$Source + PowerShellCustomFunctionAttribute() { $this.RequiresElevation = $false; $this.Source = "Microsoft.PowerShell.Crescendo" } + PowerShellCustomFunctionAttribute([bool]$rElevation) { + $this.RequiresElevation = $rElevation + $this.Source = "Microsoft.PowerShell.Crescendo" + } +} + + + +function Get-OpSecretList +{ +[PowerShellCustomFunctionAttribute(RequiresElevation=$False)] +[CmdletBinding()] + +param( ) + +BEGIN { + $__PARAMETERMAP = @{} + $__outputHandlers = @{ + Default = @{ StreamOutput = $True; Handler = { $input | ConvertFrom-Json } } + } +} + +PROCESS { + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'item' + $__commandArgs += 'list' + $__commandArgs += '--format' + $__commandArgs += 'json' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message op + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("op $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "op")) { + throw "Cannot find executable 'op'" + } + if ( $__handlerInfo.StreamOutput ) { + & "op" $__commandArgs | & $__handler + } + else { + $result = & "op" $__commandArgs + & $__handler $result + } + } + } # end PROCESS + +<# + + +.DESCRIPTION +List secrets + +#> +} + + + + +function Get-OpSecret +{ +[PowerShellCustomFunctionAttribute(RequiresElevation=$False)] +[CmdletBinding()] + +param( +[Parameter(Position=0)] +[string]$Name + ) + +BEGIN { + $__PARAMETERMAP = @{ + Name = @{ + OriginalName = '' + OriginalPosition = '0' + Position = '0' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $False + } + } + + $__outputHandlers = @{ + Default = @{ StreamOutput = $True; Handler = { $input | ConvertFrom-Json } } + } +} + +PROCESS { + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'item' + $__commandArgs += 'get' + $__commandArgs += '--format' + $__commandArgs += 'json' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message op + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("op $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "op")) { + throw "Cannot find executable 'op'" + } + if ( $__handlerInfo.StreamOutput ) { + & "op" $__commandArgs | & $__handler + } + else { + $result = & "op" $__commandArgs + & $__handler $result + } + } + } # end PROCESS + +<# + + +.DESCRIPTION +Get a secret + +.PARAMETER Name +An item's name or its unique identifier + + + +#> +} + + + + +function New-OpSecret +{ +[PowerShellCustomFunctionAttribute(RequiresElevation=$False)] +[CmdletBinding()] + +param( +[ValidateSet('login','password')] +[Parameter()] +[PSDefaultValue(Value="login")] +[string]$Category = "login", +[Parameter()] +[string]$Name, +[Parameter()] +[string]$Username, +[Parameter(Mandatory=$true)] +[SecureString]$Password + ) + +BEGIN { + $__PARAMETERMAP = @{ + Category = @{ + OriginalName = '--category=' + OriginalPosition = '0' + Position = '2147483647' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $True + } + Name = @{ + OriginalName = '--title=' + OriginalPosition = '0' + Position = '2147483647' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $True + } + Username = @{ + OriginalName = '--username=' + OriginalPosition = '0' + Position = '2147483647' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $True + } + Password = @{ + OriginalName = 'password=' + OriginalPosition = '0' + Position = '2147483647' + ParameterType = 'SecureString' + ApplyToExecutable = $False + NoGap = $True + } + } + + $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } +} + +PROCESS { + $__boundParameters = $PSBoundParameters + $__defaultValueParameters = $PSCmdlet.MyInvocation.MyCommand.Parameters.Values.Where({$_.Attributes.Where({$_.TypeId.Name -eq "PSDefaultValueAttribute"})}).Name + $__defaultValueParameters.Where({ !$__boundParameters["$_"] }).ForEach({$__boundParameters["$_"] = get-variable -value $_}) + $__commandArgs = @() + $MyInvocation.MyCommand.Parameters.Values.Where({$_.SwitchParameter -and $_.Name -notmatch "Debug|Whatif|Confirm|Verbose" -and ! $__boundParameters[$_.Name]}).ForEach({$__boundParameters[$_.Name] = [switch]::new($false)}) + if ($__boundParameters["Debug"]){wait-debugger} + $__commandArgs += 'item' + $__commandArgs += 'create' + foreach ($paramName in $__boundParameters.Keys| + Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| + Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { + $value = $__boundParameters[$paramName] + $param = $__PARAMETERMAP[$paramName] + if ($param) { + if ($value -is [switch]) { + if ($value.IsPresent) { + if ($param.OriginalName) { $__commandArgs += $param.OriginalName } + } + elseif ($param.DefaultMissingValue) { $__commandArgs += $param.DefaultMissingValue } + } + elseif ( $param.NoGap ) { + $pFmt = "{0}{1}" + if($value -match "\s") { $pFmt = "{0}""{1}""" } + $__commandArgs += $pFmt -f $param.OriginalName, $value + } + else { + if($param.OriginalName) { $__commandArgs += $param.OriginalName } + $__commandArgs += $value | Foreach-Object {$_} + } + } + } + $__commandArgs = $__commandArgs | Where-Object {$_ -ne $null} + if ($__boundParameters["Debug"]){wait-debugger} + if ( $__boundParameters["Verbose"]) { + Write-Verbose -Verbose -Message op + $__commandArgs | Write-Verbose -Verbose + } + $__handlerInfo = $__outputHandlers[$PSCmdlet.ParameterSetName] + if (! $__handlerInfo ) { + $__handlerInfo = $__outputHandlers["Default"] # Guaranteed to be present + } + $__handler = $__handlerInfo.Handler + if ( $PSCmdlet.ShouldProcess("op $__commandArgs")) { + # check for the application and throw if it cannot be found + if ( -not (Get-Command -ErrorAction Ignore "op")) { + throw "Cannot find executable 'op'" + } + if ( $__handlerInfo.StreamOutput ) { + & "op" $__commandArgs | & $__handler + } + else { + $result = & "op" $__commandArgs + & $__handler $result + } + } + } # end PROCESS + +<# + + +.DESCRIPTION +Create a new secret + +.PARAMETER Category +Specify which template to use + + +.PARAMETER Name + + + +.PARAMETER Username + + + +.PARAMETER Password +The password + + + +#> +} -. $CommandBuilderFile -. $OpCommandFile -Get-ChildItem -Path $publicFolder | ForEach-Object { - . $_.FullName -} \ No newline at end of file diff --git a/tests/class_CommandBuilder.Tests.ps1 b/tests/class_CommandBuilder.Tests.ps1 deleted file mode 100644 index 1bb877b..0000000 --- a/tests/class_CommandBuilder.Tests.ps1 +++ /dev/null @@ -1,251 +0,0 @@ -Describe 'CommandBuilder' { - BeforeAll { - $class = Join-Path -Path $PSScriptRoot -ChildPath '..' -AdditionalChildPath 'src', 'class', 'CommandBuilder.ps1' - . $class - } - Context "CommandArgument" { - Context "Defaults" { - BeforeAll { - $sut = [CommandArgument]::New('test') - } - It 'Should have a name property' { - $sut.Name | Should -Be 'test' - } - It 'Should not have a value' { - $sut.HasValue | Should -BeFalse - $sut.Value | Should -BeNullOrEmpty - } - It 'Should have no seperator' { - $sut.Seperator | Should -BeNullOrEmpty - } - It 'ToString return should be Name' { - $result = $sut.ToString() - $result | Should -Be 'test' - } - } - Context "Defaults with single Value" { - BeforeAll { - $sut = [CommandArgument]::New('test', 'value') - } - - It 'Should have a name property' { - $sut.Name | Should -Be 'test' - } - It 'Should have a value' { - $sut.HasValue | Should -BeTrue - $sut.Value | Should -Be 'value' - } - It 'Should have a space seperator' { - $sut.Seperator | Should -Be ' ' - } - It 'ToString return should be Name Value' { - $result = $sut.ToString() - $result | Should -Be 'test value' - } - } - Context "Defaults with multi Value" { - BeforeAll { - $sut = [CommandArgument]::New('test', @('value1', 'value2')) - } - - It 'Should have a name property' { - $sut.Name | Should -Be 'test' - } - It 'Should have a value array' { - $sut.HasValue | Should -BeTrue - $sut.Value.Count | Should -Be 2 - } - It 'Should have a space seperator' { - $sut.Seperator | Should -Be ' ' - } - It 'ToString return should be Name Value' { - $result = $sut.ToString() - $result | Should -Be 'test value1,value2' - } - } - Context "Seperator" { - BeforeAll { - $sut = [CommandArgument]::New('test', 'value') - $sut = $sut.SetSeparator(':') - } - - It 'Should have a space seperator' { - $sut.Seperator | Should -Be ':' - } - It 'ToString return should be Name Value' { - $result = $sut.ToString() - $result | Should -Be 'test:value' - } - } - Context "Reduce with one value" { - BeforeAll { - $sut = [CommandArgument]::New('test', 'value') - $sut = $sut.SetValueReduce( { $args -join ' ' }) - } - - It 'Should have a space seperator' { - $sut.ValueReduce.ToString().Trim() | Should -Be '$args -join '' ''' - } - It 'ToString return should be Name Value' { - $result = $sut.ToString() - $result | Should -Be 'test value' - } - It 'AsMultipleArguments should also be Name Value' { - $result = $sut.AsMultipleArguments() - - $result.Count | Should -Be 1 - $result[0].ToString() | Should -Be 'test value' - } - } - Context "Reduce with multiple values" { - BeforeAll { - $sut = [CommandArgument]::New('test', @('value1', 'value2')) - $sut = $sut.SetValueReduce( { $args -join ' ' } ) - } - - It 'ToString return should be Name Value Value' { - $result = $sut.ToString() - - $sut.Value.Count | Should -Be 2 - $sut.HasValue | Should -BeTrue - $result | Should -Be 'test value1 value2' - } - It 'Should throw if Reduce doesn''t reference $args' { - { $sut.SetValueReduce( { $_ -join ' ' } ) } | Should -Throw 'ScriptBlock must contain "$args" variable.' - } - It 'AsMultipleArguments should return a List of Argument' { - $result = $sut.AsMultipleArguments() - - $result.Count | Should -Be 2 - $result[0].ToString() | Should -Be 'test value1' - $result[1].ToString() | Should -Be 'test value2' - } - } - Context 'Add Value' { - BeforeAll { - $sut = [CommandArgument]::New('test') - - } - It 'Should not have a value' { - $sut.HasValue | Should -BeFalse - $sut.Seperator | Should -BeNullOrEmpty - $sut.Value | Should -BeNullOrEmpty - } - It 'AddValue should add to Value array' { - $new = $sut.AddValue('value') - - $new.Value.Count | Should -Be 1 - $new.HasValue | Should -BeTrue - $result = $new.ToString() - $result | Should -Be 'test value' - } - } - } - Context "ThingToRun" { - Context "No arguments" { - BeforeAll { - $sut = [CommandBuilder]::New('test') - } - It 'Should have a Name' { - $sut.Name | Should -Be 'test' - } - It 'Should have no arguments' { - $sut.ArgumentList.count | Should -Be 0 - } - } - Context "With one argument" { - BeforeAll { - $sut = [CommandBuilder]::New('test') - $arg = [CommandArgument]::New('-arg', 'value') - $sut = $sut.AddArgument($arg) - } - It 'Should have a Name' { - $sut.Name | Should -Be 'test' - - } - It 'Should an argument' { - $sut.ArgumentList.count | Should -Be 1 - $sut.ToString() | Should -Be 'test -arg value' - } - } - Context "With multiple argument" { - BeforeAll { - $sut = [CommandBuilder]::New('test') - $arg1 = [CommandArgument]::New('-arg', 'value') - $arg2 = [CommandArgument]::New('-flag') - $sut = $sut.AddArgument($arg1).AddArgument($arg2) - - } - It 'Should have a Name' { - $sut.Name | Should -Be 'test' - - } - It 'Should an argument' { - $sut.ArgumentList.count | Should -Be 2 - $sut.ToString() | Should -Be 'test -arg value -flag' - } - } - Context "With one sensitive value" { - BeforeEach { - $sut = [CommandBuilder]::New('test') - $arg = [CommandArgument]::New('-arg', 'value') - $arg.IsSensitive = $true - $sut = $sut.AddArgument($arg) - } - It 'Should have a Name' { - $sut.Name | Should -Be 'test' - - } - It 'Should mask an argument' { - $sut.ArgumentList.count | Should -Be 1 - $sut.ToString($true) | Should -Be 'test -arg *****' - } - It 'Should not mask by default' { - $sut.ArgumentList.count | Should -Be 1 - $sut.ToString() | Should -Be 'test -arg value' - } - } - Context "With multiple values, one sensitive" { - BeforeEach { - $sut = [CommandBuilder]::New('test') - $arg1 = [CommandArgument]::New('-arg', 'value') - $arg1.IsSensitive = $true - $arg2 = [CommandArgument]::New('-arg2', 'value2') - $sut = $sut.AddArgument($arg1).AddArgument($arg2) - } - It 'Should only the sensetive argument' { - $sut.ArgumentList.count | Should -Be 2 - $sut.ToString($true) | Should -Be 'test -arg ***** -arg2 value2' - } - } - Context 'Helper methods' { - It '.AddArgument($name)' { - $sut = [CommandBuilder]::New('test').AddArgument('-flag') - $sut.ArgumentList.count | Should -Be 1 - $sut.ToString() | Should -Be 'test -flag' - } - It '.AddArgument($name, $value)' { - $sut = [CommandBuilder]::New('test').AddArgument('-flag', 'value') - $sut.ArgumentList.count | Should -Be 1 - $sut.ToString() | Should -Be 'test -flag value' - } - It '.AddArgument($name, $value, $seperator)' { - $sut = [CommandBuilder]::New('test').AddArgument('-flag', 'value', ':') - $sut.ArgumentList.count | Should -Be 1 - $sut.ToString() | Should -Be 'test -flag:value' - } - } - Context 'Get ProcessStartInfo' { - BeforeAll { - $sut = [CommandBuilder]::New('test').AddArgument('-flag') - } - It 'Should return a ProcessStartInfo object' { - $result = $sut.GetProcessStartInfo() - - $result.FileName | Should -Be 'test' - $result.ArgumentList | Should -Be '-flag' - } - - } - } -} \ No newline at end of file diff --git a/tests/class_OpCommand.Tests.ps1 b/tests/class_OpCommand.Tests.ps1 deleted file mode 100644 index 294e417..0000000 --- a/tests/class_OpCommand.Tests.ps1 +++ /dev/null @@ -1,267 +0,0 @@ -Describe 'OpCommand' { - BeforeAll { - $class = Join-Path -Path $PSScriptRoot -ChildPath '..' -AdditionalChildPath 'src', 'class', 'CommandBuilder.ps1' - . $class - } - Context 'Constructor' { - BeforeAll { - $sut = [OpCommand]::New() - } - It 'Should be a valid CommandBuilder child object' { - $sut.Name | Should -Be 'op' - } - It 'Should not have a value' { - $sut.HasValue | Should -BeFalse - $sut.Value | Should -BeNullOrEmpty - } - It 'Should have no seperator' { - $sut.Seperator | Should -BeNullOrEmpty - } - It 'ToString return should be Name' { - $result = $sut.ToString() - $result | Should -Be 'op' - } - It 'Has message data' { - $sut.LocalizedMessage | Should -Not -BeNullOrEmpty - $sut.LocalizedMessage.Count | Should -Be 2 - } - } - Context 'List' { - BeforeAll { - $sut = [OpCommandList]::New() - } - It 'Should be a valid CommandBuilder child object' { - $sut.Name | Should -Be 'op' - } - It 'Should not have a value' { - $sut.HasValue | Should -BeFalse - $sut.Value | Should -BeNullOrEmpty - } - It 'Should have no seperator' { - $sut.Seperator | Should -BeNullOrEmpty - } - It 'ToString return should be Name' { - $result = $sut.ToString() - $result | Should -Be 'op list' - } - } - Context 'List Items' { - BeforeAll { - $sut = [OpCommandListItem]::New() - } - It 'Should be a valid CommandBuilder child object' { - $sut.Name | Should -Be 'op' - } - It 'ToString return should be Name' { - $result = $sut.ToString() - $result | Should -Be 'op list items' - } - } - Context 'List Items with Arguments' { - BeforeEach { - $sut = [OpCommandListItem]::New() - } - - It 'With Vault' { - $result = $sut.WithVault('test_vault').ToString() - $result | Should -Be 'op list items --vault test_vault' - } - It 'With one Category' { - $result = $sut.WithCategory('Login').ToString() - $result | Should -Be 'op list items --categories Login' - } - It 'With more than one Category' { - $result = $sut.WithCategory(@('Login', 'Password')).ToString() - $result | Should -Be 'op list items --categories Login,Password' - } - It 'With more than one Category using first overload' { - $result = $sut.WithCategory('Login', 'Password').ToString() - $result | Should -Be 'op list items --categories Login,Password' - } - It 'With more than one Category using second overload' { - $result = $sut.WithCategory('Login', 'Password', 'Server').ToString() - $result | Should -Be 'op list items --categories Login,Password,Server' - } - It 'With one Tag' { - $result = $sut.WithTag('tag1').ToString() - $result | Should -Be 'op list items --tags tag1' - } - It 'With more than one Tag' { - $result = $sut.WithTag(@('tag1', 'tag2')).ToString() - $result | Should -Be 'op list items --tags tag1,tag2' - } - It 'With more than one Tag using first overload' { - $result = $sut.WithTag('tag1', 'tag2').ToString() - $result | Should -Be 'op list items --tags tag1,tag2' - } - It 'With more than one Tag using second overload' { - $result = $sut.WithTag('tag1', 'tag2', 'tag3').ToString() - $result | Should -Be 'op list items --tags tag1,tag2,tag3' - } - It 'With Category and Tag' { - $result = $sut.WithCategory('Login', 'Password').WithTag('tag1', 'tag2').ToString() - $result | Should -Be 'op list items --categories Login,Password --tags tag1,tag2' - } - It 'With Category, Tag and Vault' { - $result = $sut.WithCategory('Login', 'Password').WithTag('tag1', 'tag2').WithVault('test_vault').ToString() - $result | Should -Be 'op list items --categories Login,Password --tags tag1,tag2 --vault test_vault' - } - } - Context 'List Item as ProcessStartInfo' { - BeforeEach { - $sut = [OpCommandListItem]::New().WithCategory('Login', 'Password').WithTag('tag1', 'tag2').WithVault('test_vault').GetProcessStartInfo() - } - It 'Is as [Diagnostics.ProcessStartInfo]' { - $sut | Should -BeOfType Diagnostics.ProcessStartInfo - } - It 'Should have valid properties' { - $sut.FileName = 'op' - $sut.RedirectStandardError = $true - $sut.RedirectStandardOutput = $true - } - It 'Should have all arguments' { - $sut.ArgumentList.Count | Should -Be 8 - $sut.ArgumentList[0] | Should -Be 'list' - $sut.ArgumentList[1] | Should -Be 'items' - $sut.ArgumentList[2] | Should -Be '--categories' - $sut.ArgumentList[3] | Should -Be 'Login,Password' - $sut.ArgumentList[4] | Should -Be '--tags' - $sut.ArgumentList[5] | Should -Be 'tag1,tag2' - $sut.ArgumentList[6] | Should -Be '--vault' - $sut.ArgumentList[7] | Should -Be 'test_vault' - } - } - Context 'Get Item with Arguments' { - BeforeEach { - $sut = [OpCommandGetItem]::New('test_secret') - } - It 'Constructor' { - $result = $sut.ToString() - $result | Should -Be 'op get item test_secret' - } - It 'With Vault' { - $result = $sut.WithVault('test_vault').ToString() - $result | Should -Be 'op get item test_secret --vault test_vault' - } - It 'With one Field' { - $result = $sut.WithField('website').ToString() - $result | Should -Be 'op get item test_secret --fields website' - } - It 'With more than one Field' { - $result = $sut.WithField(@('website', 'username')).ToString() - $result | Should -Be 'op get item test_secret --fields website,username' - } - It 'With more than one Field using first overload' { - $result = $sut.WithField('website', 'username').ToString() - $result | Should -Be 'op get item test_secret --fields website,username' - } - It 'With more than one Field using second overload' { - $result = $sut.WithField('website', 'username', 'Server').ToString() - $result | Should -Be 'op get item test_secret --fields website,username,Server' - } - - It 'With Field and Format Json' { - $result = $sut.WithField('website', 'username').WithJsonFormat().ToString() - $result | Should -Be 'op get item test_secret --fields website,username --format JSON' - } - It 'With Field and Format CSV' { - $result = $sut.WithField('website', 'username').WithCsvFormat().ToString() - $result | Should -Be 'op get item test_secret --fields website,username --format CSV' - } - It 'Format without Field errors' { - $ErrorActionPreference = 'Stop' - { $sut.WithCsvFormat().ToString() } | Should -Throw 'Format can only be used with Fields' - - } - It 'Can''t add Format without Field' { - $ErrorActionPreference = 'SilentlyContinue' - $result = $sut.WithCsvFormat().ToString() - $result | Should -Be 'op get item test_secret' - } - It 'Change Format errors' { - $ErrorActionPreference = 'Stop' - { $sut.WithField('website', 'username').WithJsonFormat().WithCsvFormat() } | Should -Throw 'Format has already been set' - } - It 'Can''t change Format' { - $ErrorActionPreference = 'SilentlyContinue' - $result = $sut.WithField('website', 'username').WithJsonFormat().WithCsvFormat().ToString() - $result | Should -Be 'op get item test_secret --fields website,username --format JSON' - } - } - Context 'Get Item as ProcessStartInfo' { - BeforeEach { - $sut = [OpCommandGetItem]::New('test_secret').WithField('website', 'username').WithJsonFormat().GetProcessStartInfo() - } - It 'Is as [Diagnostics.ProcessStartInfo]' { - $sut | Should -BeOfType Diagnostics.ProcessStartInfo - } - It 'Should have valid properties' { - $sut.FileName = 'op' - $sut.RedirectStandardError = $true - $sut.RedirectStandardOutput = $true - } - It 'Should have all arguments' { - $sut.ArgumentList.Count | Should -Be 7 - $sut.ArgumentList[0] | Should -Be 'get' - $sut.ArgumentList[1] | Should -Be 'item' - $sut.ArgumentList[2] | Should -Be 'test_secret' - $sut.ArgumentList[3] | Should -Be '--fields' - $sut.ArgumentList[4] | Should -Be 'website,username' - $sut.ArgumentList[5] | Should -Be '--format' - $sut.ArgumentList[6] | Should -Be 'JSON' - } - } - Context 'ParseError' { - BeforeEach { - $commandHelpMessage = @' -To list objects and events, use one of the `list` subcommands. - -Usage: - op list [command] - -Available Commands: - documents Get a list of documents - events Get a list of events from the Activity Log - groups Get a list of groups - items Get a list of items - templates Get a list of templates - users Get the list of users - vaults Get a list of vaults - -Flags: - -h, --help get help with list - -Global Flags: - --account shorthand use the account with this shorthand - --cache store and use cached information - --config directory use this configuration directory - --session token authenticate with this session token - -Use "op list [command] --help" for more information about a command. -'@ - $signinError = '[ERROR] 2020/12/11 13:48:31 session expired, sign in to create a new session' - $signinError2 = '[ERROR] 2020/12/11 15:01:17 You are not currently signed in. Please run `op signin --help` for instructions' - $ErrorActionPreference = 'Stop' - } - It 'Base passes the message through to Error' { - $sut = [CommandBuilder]::new('command') - $sut.ParseStdErr($signinError) | Should -Be '[ERROR] 2020/12/11 13:48:31 session expired, sign in to create a new session' - } - It 'Parses Command Help should have no Error message' { - $sut = [OpCommand]::new() - $sut.ParseStdErr($commandHelpMessage) | Should -BeNullOrEmpty - } - It 'Parses Signin Error should an Error message' { - $sut = [OpCommand]::new() - $sut.ParseStdErr($signinError) | Should -Be 'errorSignin' - } - It 'Parses Signin Error 2 should an Error message' { - $sut = [OpCommand]::new() - $sut.ParseStdErr($signinError2) | Should -Be 'errorSignin' - } - It 'Child class should parse Error message' { - $sut = [OpCommandListItem]::new() - $sut.ParseStdErr($signinError) | Should -Be 'errorSignin' - } - } -} From 1cf7e04651ae2decc016f4fad21049c552cfd648 Mon Sep 17 00:00:00 2001 From: Chris Hunt <chunt@stackoverflow.com> Date: Fri, 16 Sep 2022 11:11:14 -0400 Subject: [PATCH 2/2] Handle --vault and structure output --- op.crescendo.config.json | 35 +++++++++++++++++++++-- src/op-powershell.psd1 | 4 +-- src/op-powershell.psm1 | 62 ++++++++++++++++++++++++++++++++++++---- 3 files changed, 90 insertions(+), 11 deletions(-) diff --git a/op.crescendo.config.json b/op.crescendo.config.json index cb81451..f73c420 100644 --- a/op.crescendo.config.json +++ b/op.crescendo.config.json @@ -12,10 +12,18 @@ "json" ], "Description": "List secrets", + "Parameters": [ + { + "Name": "Vault", + "OriginalName": "--vault=", + "ParameterType": "string", + "NoGap": true + } + ], "OutputHandlers": [ { "ParameterSetName": "Default", - "Handler": "$input | ConvertFrom-Json", + "Handler": "$input | ConvertFrom-Json | Foreach-Object {[PSCustomObject]@{PSTypeName='OpLoginSummary'; Id=$_.id; Name=$_.title; Vault=$_.vault.name; Created=$_.created_at; Updated=$_.updated_at} }", "StreamOutput": true } ] @@ -38,12 +46,18 @@ "Position": 0, "Description": "An item's name or its unique identifier", "ParameterType": "string" + }, + { + "Name": "Vault", + "OriginalName": "--vault=", + "ParameterType": "string", + "NoGap": true } ], "OutputHandlers": [ { "ParameterSetName": "Default", - "Handler": "$input | ConvertFrom-Json", + "Handler": "$input | ConvertFrom-Json | Foreach-Object {[PSCustomObject]@{PSTypeName='OpLogin'; Id=$_.id; Name=$_.title; UserName=$_.fields.Where({$_.id -eq 'username'}).value; Vault=$_.vault.name; Created=$_.created_at; Updated=$_.updated_at}}", "StreamOutput": true } ] @@ -54,7 +68,9 @@ "OriginalName": "op", "OriginalCommandElements": [ "item", - "create" + "create", + "--format", + "json" ], "Description": "Create a new secret", "Parameters": [ @@ -75,6 +91,12 @@ "ParameterType": "string", "NoGap": true }, + { + "Name": "Vault", + "OriginalName": "--vault=", + "ParameterType": "string", + "NoGap": true + }, { "Name": "Username", "OriginalName": "--username=", @@ -90,6 +112,13 @@ "Mandatory": true, "NoGap": true } + ], + "OutputHandlers": [ + { + "ParameterSetName": "Default", + "Handler": "$input | ConvertFrom-Json", + "StreamOutput": true + } ] } ] diff --git a/src/op-powershell.psd1 b/src/op-powershell.psd1 index 19a4aa3..06cfa74 100644 --- a/src/op-powershell.psd1 +++ b/src/op-powershell.psd1 @@ -18,7 +18,7 @@ ModuleVersion = '0.0.1' # CompatiblePSEditions = @() # ID used to uniquely identify this module -GUID = '6151db2d-c545-4c9a-8f90-65822264bddc' +GUID = '6c51033a-7546-44c9-a8e1-201d53b411e2' # Author of this module Author = 'chunt' @@ -125,7 +125,7 @@ PrivateData = @{ CrescendoVersion = '1.0.0' # CrescendoGenerated - CrescendoGenerated = '09/16/2022 10:46:23' + CrescendoGenerated = '09/16/2022 11:05:56' } # End of PrivateData hashtable diff --git a/src/op-powershell.psm1 b/src/op-powershell.psm1 index bfc8523..55df3e6 100644 --- a/src/op-powershell.psm1 +++ b/src/op-powershell.psm1 @@ -16,12 +16,25 @@ function Get-OpSecretList [PowerShellCustomFunctionAttribute(RequiresElevation=$False)] [CmdletBinding()] -param( ) +param( +[Parameter()] +[string]$Vault + ) BEGIN { - $__PARAMETERMAP = @{} + $__PARAMETERMAP = @{ + Vault = @{ + OriginalName = '--vault=' + OriginalPosition = '0' + Position = '2147483647' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $True + } + } + $__outputHandlers = @{ - Default = @{ StreamOutput = $True; Handler = { $input | ConvertFrom-Json } } + Default = @{ StreamOutput = $True; Handler = { $input | ConvertFrom-Json | Foreach-Object {[PSCustomObject]@{Id=$_.id; Name=$_.title; Vault=$_.vault.name; Created=$_.created_at; Updated=$_.updated_at} } } } } } @@ -91,6 +104,11 @@ PROCESS { .DESCRIPTION List secrets +.PARAMETER Vault + + + + #> } @@ -104,7 +122,9 @@ function Get-OpSecret param( [Parameter(Position=0)] -[string]$Name +[string]$Name, +[Parameter()] +[string]$Vault ) BEGIN { @@ -117,10 +137,18 @@ BEGIN { ApplyToExecutable = $False NoGap = $False } + Vault = @{ + OriginalName = '--vault=' + OriginalPosition = '0' + Position = '2147483647' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $True + } } $__outputHandlers = @{ - Default = @{ StreamOutput = $True; Handler = { $input | ConvertFrom-Json } } + Default = @{ StreamOutput = $True; Handler = { $input | ConvertFrom-Json | Foreach-Object {[PSCustomObject]@{Id=$_.id; Name=$_.title; UserName=$_.fields.Where({$_.id -eq 'username'}).value; Vault=$_.vault.name; Created=$_.created_at; Updated=$_.updated_at}} } } } } @@ -194,6 +222,10 @@ Get a secret An item's name or its unique identifier +.PARAMETER Vault + + + #> } @@ -214,6 +246,8 @@ param( [Parameter()] [string]$Name, [Parameter()] +[string]$Vault, +[Parameter()] [string]$Username, [Parameter(Mandatory=$true)] [SecureString]$Password @@ -237,6 +271,14 @@ BEGIN { ApplyToExecutable = $False NoGap = $True } + Vault = @{ + OriginalName = '--vault=' + OriginalPosition = '0' + Position = '2147483647' + ParameterType = 'string' + ApplyToExecutable = $False + NoGap = $True + } Username = @{ OriginalName = '--username=' OriginalPosition = '0' @@ -255,7 +297,9 @@ BEGIN { } } - $__outputHandlers = @{ Default = @{ StreamOutput = $true; Handler = { $input } } } + $__outputHandlers = @{ + Default = @{ StreamOutput = $True; Handler = { $input | ConvertFrom-Json } } + } } PROCESS { @@ -267,6 +311,8 @@ PROCESS { if ($__boundParameters["Debug"]){wait-debugger} $__commandArgs += 'item' $__commandArgs += 'create' + $__commandArgs += '--format' + $__commandArgs += 'json' foreach ($paramName in $__boundParameters.Keys| Where-Object {!$__PARAMETERMAP[$_].ApplyToExecutable}| Sort-Object {$__PARAMETERMAP[$_].OriginalPosition}) { @@ -330,6 +376,10 @@ Specify which template to use +.PARAMETER Vault + + + .PARAMETER Username