diff --git a/op.crescendo.config.json b/op.crescendo.config.json new file mode 100644 index 0000000..f73c420 --- /dev/null +++ b/op.crescendo.config.json @@ -0,0 +1,125 @@ +{ + "$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", + "Parameters": [ + { + "Name": "Vault", + "OriginalName": "--vault=", + "ParameterType": "string", + "NoGap": true + } + ], + "OutputHandlers": [ + { + "ParameterSetName": "Default", + "Handler": "$input | ConvertFrom-Json | Foreach-Object {[PSCustomObject]@{PSTypeName='OpLoginSummary'; Id=$_.id; Name=$_.title; Vault=$_.vault.name; Created=$_.created_at; Updated=$_.updated_at} }", + "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" + }, + { + "Name": "Vault", + "OriginalName": "--vault=", + "ParameterType": "string", + "NoGap": true + } + ], + "OutputHandlers": [ + { + "ParameterSetName": "Default", + "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 + } + ] + }, + { + "Verb": "New", + "Noun": "OpSecret", + "OriginalName": "op", + "OriginalCommandElements": [ + "item", + "create", + "--format", + "json" + ], + "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": "Vault", + "OriginalName": "--vault=", + "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 + } + ], + "OutputHandlers": [ + { + "ParameterSetName": "Default", + "Handler": "$input | ConvertFrom-Json", + "StreamOutput": 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\] (?\d{4}\W\d{1,2}\W\d{1,2}) (?