From a7a03a71487f8d4f96ec58b937ca368e9184f7d0 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Thu, 29 Oct 2020 16:31:26 -0700 Subject: [PATCH 1/7] First set of preview3 changes --- CHANGELOG.md | 14 + help/Set-SecretStoreConfiguration.md | 15 + src/Microsoft.PowerShell.SecretStore.psd1 | 6 +- .../Microsoft.PowerShell.SecretStore.csproj | 6 +- src/code/SecretStore.cs | 8 +- src/code/Utils.cs | 431 ++++++++++-------- 6 files changed, 295 insertions(+), 185 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3b17703..9449837 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # CHANGELOG +## 0.5.3-Preview3 - 2020-11-4 + +### Fixes + +### Changes + +- `Set-SecretStoreConfiguration` now has a `-PassThru` parameter to write the store configuration object to the pipeline, and no longer writes the configuration object by default (Issue #25). + +- A `-PasswordTimeout` value of zero now allows the provided password to be used only once (Issue #30). + +- When setting a password, an empty password is no longer accepted (Issue #31) + +### New Features + ## 0.5.2-Preview2 - 2020-10-01 ### Breaking Changes diff --git a/help/Set-SecretStoreConfiguration.md b/help/Set-SecretStoreConfiguration.md index aa04271..7023db2 100644 --- a/help/Set-SecretStoreConfiguration.md +++ b/help/Set-SecretStoreConfiguration.md @@ -96,6 +96,21 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -PassThru +When true, will write the current SecretStore configuration to the pipeline. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -PasswordTimeout Configuration option that provides the session password timeout in seconds. Takes an argument whose value determines the session password timeout in seconds. diff --git a/src/Microsoft.PowerShell.SecretStore.psd1 b/src/Microsoft.PowerShell.SecretStore.psd1 index 88207da..73cc6dc 100644 --- a/src/Microsoft.PowerShell.SecretStore.psd1 +++ b/src/Microsoft.PowerShell.SecretStore.psd1 @@ -8,10 +8,10 @@ RootModule = '.\Microsoft.PowerShell.SecretStore.dll' NestedModules = @('.\Microsoft.PowerShell.SecretStore.Extension') -RequiredModules = @('Microsoft.PowerShell.SecretManagement') +#RequiredModules = @('Microsoft.PowerShell.SecretManagement') # Version number of this module. -ModuleVersion = '0.5.2' +ModuleVersion = '0.5.3' # Supported PSEditions CompatiblePSEditions = @('Core') @@ -68,7 +68,7 @@ PrivateData = @{ # ReleaseNotes = '' # Prerelease string of this module - Prerelease = 'preview2' + Prerelease = 'preview3' # Flag to indicate whether the module requires explicit user acceptance for install/update/save # RequireLicenseAcceptance = $false diff --git a/src/code/Microsoft.PowerShell.SecretStore.csproj b/src/code/Microsoft.PowerShell.SecretStore.csproj index f0c1a4d..99f6046 100644 --- a/src/code/Microsoft.PowerShell.SecretStore.csproj +++ b/src/code/Microsoft.PowerShell.SecretStore.csproj @@ -5,9 +5,9 @@ Library Microsoft.PowerShell.SecretStore Microsoft.PowerShell.SecretStore - 0.5.2.0 - 0.5.2 - 0.5.2 + 0.5.3.0 + 0.5.3 + 0.5.3 netstandard2.0 diff --git a/src/code/SecretStore.cs b/src/code/SecretStore.cs index d7b48c7..59d740f 100644 --- a/src/code/SecretStore.cs +++ b/src/code/SecretStore.cs @@ -135,6 +135,9 @@ public sealed class SetSecretStoreConfiguration : PSCmdlet [Parameter(ParameterSetName = DefaultParameterSet)] public SwitchParameter Default { get; set; } + [Parameter] + public SwitchParameter PassThru { get; set; } + [Parameter] public SwitchParameter Force { get; set; } @@ -202,7 +205,10 @@ protected override void EndProcessing() this)); } - WriteObject(newConfigData); + if (PassThru.IsPresent) + { + WriteObject(newConfigData); + } } #endregion diff --git a/src/code/Utils.cs b/src/code/Utils.cs index b4f013b..78c7760 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -197,6 +197,11 @@ public static SecureString PromptForPassword( // Initial prompt cmdlet.Host.UI.WriteLine("Enter password:"); password = cmdlet.Host.UI.ReadLineAsSecureString(); + if (password.Length == 0) + { + cmdlet.Host.UI.WriteLine("\nThe entered password cannot be empty. Please re-enter the password.\n"); + continue; + } if (verifyPassword) { @@ -978,7 +983,14 @@ internal SecureString Password throw new PasswordRequiredException(Utils.PasswordRequiredMessage); } - return _password?.Copy() ?? null; + var returnPassword = _password?.Copy() ?? null; + if (_password != null && _configData.PasswordTimeout == 0) + { + // PasswordTimeout == 0, means password is only used once. + _password = null; + } + + return returnPassword; } } } @@ -1148,6 +1160,7 @@ public bool WriteBlob( byte[] blob, string typeName, Dictionary attributes, + SecureString password, out string errorMsg) { if (EnumerateBlobs( @@ -1160,6 +1173,7 @@ public bool WriteBlob( blob, typeName, attributes, + password, out errorMsg); } @@ -1168,11 +1182,13 @@ public bool WriteBlob( blob, typeName, attributes, + password, out errorMsg); } public bool ReadBlob( string name, + SecureString password, out byte[] blob, out SecureStoreMetadata metaData, out string errorMsg) @@ -1200,18 +1216,10 @@ public bool ReadBlob( } // Decrypt blob - var password = Password; - try - { - blob = CryptoUtils.DecryptWithKey( - passWord: password, - key: key, - data: encryptedBlob); - } - finally - { - password?.Clear(); - } + blob = CryptoUtils.DecryptWithKey( + passWord: password, + key: key, + data: encryptedBlob); return true; } @@ -1251,6 +1259,7 @@ public bool EnumerateBlobs( public bool DeleteBlob( string name, + SecureString password, out string errorMsg) { lock (_syncObject) @@ -1289,18 +1298,10 @@ public bool DeleteBlob( } // Write to file - var password = Password; - try - { - return SecureStoreFile.WriteFile( - password: password, - data: _data, - out errorMsg); - } - finally - { - password?.Clear(); - } + return SecureStoreFile.WriteFile( + password: password, + data: _data, + out errorMsg); } public bool UpdateConfigData( @@ -1407,20 +1408,12 @@ public bool UpdateConfigData( public void UpdateDataFromFile() { SecureStoreData data; - SecureString password = Password; - try + if (!SecureStoreFile.ReadFile( + password: _password, + data: out data, + out string _)) { - if (!SecureStoreFile.ReadFile( - password: Password, - data: out data, - out string _)) - { - data = SecureStoreData.CreateEmpty(); - } - } - finally - { - password?.Clear(); + data = SecureStoreData.CreateEmpty(); } lock (_syncObject) @@ -1543,56 +1536,49 @@ private bool WriteBlobImpl( byte[] blob, string typeName, Dictionary attributes, + SecureString password, out string errorMsg) { - var password = Password; - try - { - var newData = new SecureStoreData(); - newData.MetaData = _data.MetaData; - newData.Key = _data.Key; + var newData = new SecureStoreData(); + newData.MetaData = _data.MetaData; + newData.Key = _data.Key; - // Encrypt blob - var blobToWrite = CryptoUtils.EncryptWithKey( - passWord: password, - key: _data.Key, - data: blob); + // Encrypt blob + var blobToWrite = CryptoUtils.EncryptWithKey( + passWord: password, + key: _data.Key, + data: blob); - lock (_syncObject) - { - // Create new store blob - var oldBlob = _data.Blob; - var offset = oldBlob.Length; - var newBlob = new byte[offset + blobToWrite.Length]; - Buffer.BlockCopy(oldBlob, 0, newBlob, 0, offset); - Buffer.BlockCopy(blobToWrite, 0, newBlob, offset, blobToWrite.Length); - newData.Blob = newBlob; - - // Create new meta item - newData.MetaData.Add( - key: name, - value: new SecureStoreMetadata( - name: name, - typeName: typeName, - offset: offset, - size: blobToWrite.Length, - attributes: new ReadOnlyDictionary(attributes))); - - // Update store data - _data = newData; - CryptoUtils.ZeroOutData(oldBlob); - } - - // Write to file - return SecureStoreFile.WriteFile( - password: password, - data: _data, - out errorMsg); - } - finally + lock (_syncObject) { - password?.Clear(); + // Create new store blob + var oldBlob = _data.Blob; + var offset = oldBlob.Length; + var newBlob = new byte[offset + blobToWrite.Length]; + Buffer.BlockCopy(oldBlob, 0, newBlob, 0, offset); + Buffer.BlockCopy(blobToWrite, 0, newBlob, offset, blobToWrite.Length); + newData.Blob = newBlob; + + // Create new meta item + newData.MetaData.Add( + key: name, + value: new SecureStoreMetadata( + name: name, + typeName: typeName, + offset: offset, + size: blobToWrite.Length, + attributes: new ReadOnlyDictionary(attributes))); + + // Update store data + _data = newData; + CryptoUtils.ZeroOutData(oldBlob); } + + // Write to file + return SecureStoreFile.WriteFile( + password: password, + data: _data, + out errorMsg); } private bool ReplaceBlobImpl( @@ -1600,6 +1586,7 @@ private bool ReplaceBlobImpl( byte[] blob, string typeName, Dictionary attributes, + SecureString password, out string errorMsg) { lock (_syncObject) @@ -1607,6 +1594,7 @@ private bool ReplaceBlobImpl( // Remove old blob if (!DeleteBlob( name: name, + password: password, out errorMsg)) { errorMsg = "Unable to replace existing store item, error: " + errorMsg; @@ -1619,6 +1607,7 @@ private bool ReplaceBlobImpl( blob: blob, typeName: typeName, attributes: attributes, + password: password, out errorMsg); } } @@ -2881,41 +2870,18 @@ public bool WriteObject( T objectToWrite, out string errorMsg) { - switch (objectToWrite) + var password = _secureStore.Password; + try { - case byte[] blobToWrite: - return WriteBlob( - name, - blobToWrite, - ByteArrayType, - out errorMsg); - - case string stringToWrite: - return WriteString( - name, - stringToWrite, - out errorMsg); - - case SecureString secureStringToWrite: - return WriteSecureString( - name, - secureStringToWrite, - out errorMsg); - - case PSCredential credentialToWrite: - return WritePSCredential( - name, - credentialToWrite, - out errorMsg); - - case Hashtable hashtableToWrite: - return WriteHashtable( - name, - hashtableToWrite, - out errorMsg); - - default: - throw new InvalidOperationException("Invalid type. Types supported: byte[], string, SecureString, PSCredential, Hashtable"); + return WriteObjectImpl( + name, + objectToWrite, + password, + out errorMsg); + } + finally + { + password?.Clear(); } } @@ -2924,47 +2890,56 @@ public bool ReadObject( out object outObject, out string errorMsg) { - if (!ReadBlob( - name, - out byte[] outBlob, - out string typeName, - out errorMsg)) + var password = _secureStore.Password; + try { - outObject = null; - return false; + return ReadObjectImpl( + name, + password, + out outObject, + out errorMsg); } - - errorMsg = string.Empty; - switch (typeName) + finally { - case ByteArrayType: - outObject = outBlob; - return true; + password?.Clear(); + } + } - case StringType: - return ReadString( - outBlob, - out outObject); + public bool DeleteObject( + string name, + out string errorMsg) + { + var password = _secureStore.Password; - case SecureStringType: - return ReadSecureString( - outBlob, - out outObject); + try + { + if (!ReadObjectImpl( + name, + password, + out object outObject, + out errorMsg)) + { + return false; + } - case PSCredentialType: - return ReadPSCredential( - outBlob, - out outObject); - - case HashtableType: - return ReadHashtable( - name, - outBlob, - out outObject, - out errorMsg); + switch (outObject) + { + case Hashtable hashtable: + return DeleteHashtable( + name, + password, + out errorMsg); - default: - throw new InvalidOperationException("Invalid type. Types supported: byte[], string, SecureString, PSCredential, Hashtable"); + default: + return DeleteBlob( + name, + password, + out errorMsg); + } + } + finally + { + password?.Clear(); } } @@ -3035,32 +3010,6 @@ public bool EnumerateObjectInfo( return true; } - public bool DeleteObject( - string name, - out string errorMsg) - { - if (!ReadObject( - name: name, - outObject: out object outObject, - out errorMsg)) - { - return false; - } - - switch (outObject) - { - case Hashtable hashtable: - return DeleteHashtable( - name, - out errorMsg); - - default: - return DeleteBlob( - name, - out errorMsg); - } - } - #endregion #region Internal methods @@ -3163,10 +3112,112 @@ private static SecureString PromptForPassword( #region Blob methods + private bool ReadObjectImpl( + string name, + SecureString password, + out object outObject, + out string errorMsg) + { + if (!ReadBlob( + name, + password, + out byte[] outBlob, + out string typeName, + out errorMsg)) + { + outObject = null; + return false; + } + + errorMsg = string.Empty; + switch (typeName) + { + case ByteArrayType: + outObject = outBlob; + return true; + + case StringType: + return ReadString( + outBlob, + out outObject); + + case SecureStringType: + return ReadSecureString( + outBlob, + out outObject); + + case PSCredentialType: + return ReadPSCredential( + outBlob, + out outObject); + + case HashtableType: + return ReadHashtable( + name, + outBlob, + password, + out outObject, + out errorMsg); + + default: + throw new InvalidOperationException("Invalid type. Types supported: byte[], string, SecureString, PSCredential, Hashtable"); + } + } + + private bool WriteObjectImpl( + string name, + T objectToWrite, + SecureString password, + out string errorMsg) + { + switch (objectToWrite) + { + case byte[] blobToWrite: + return WriteBlob( + name, + blobToWrite, + ByteArrayType, + password, + out errorMsg); + + case string stringToWrite: + return WriteString( + name, + stringToWrite, + password, + out errorMsg); + + case SecureString secureStringToWrite: + return WriteSecureString( + name, + secureStringToWrite, + password, + out errorMsg); + + case PSCredential credentialToWrite: + return WritePSCredential( + name, + credentialToWrite, + password, + out errorMsg); + + case Hashtable hashtableToWrite: + return WriteHashtable( + name, + hashtableToWrite, + password, + out errorMsg); + + default: + throw new InvalidOperationException("Invalid type. Types supported: byte[], string, SecureString, PSCredential, Hashtable"); + } + } + private bool WriteBlob( string name, byte[] blob, string typeName, + SecureString password, out string errorMsg) { return _secureStore.WriteBlob( @@ -3174,17 +3225,20 @@ private bool WriteBlob( blob: blob, typeName: typeName, attributes: DefaultTag, + password: password, errorMsg: out errorMsg); } private bool ReadBlob( string name, + SecureString password, out byte[] blob, out string typeName, out string errorMsg) { if (!_secureStore.ReadBlob( name: name, + password: password, blob: out blob, metaData: out SecureStoreMetadata metadata, errorMsg: out errorMsg)) @@ -3237,10 +3291,12 @@ private bool EnumerateBlobs( private bool DeleteBlob( string name, + SecureString password, out string errorMsg) { return _secureStore.DeleteBlob( name: name, + password: password, errorMsg: out errorMsg); } @@ -3251,12 +3307,14 @@ private bool DeleteBlob( private bool WriteString( string name, string strToWrite, + SecureString password, out string errorMsg) { return WriteBlob( name: name, blob: Encoding.UTF8.GetBytes(strToWrite), typeName: StringType, + password: password, errorMsg: out errorMsg); } @@ -3285,6 +3343,7 @@ private static bool ReadString( private bool WriteStringArray( string name, string[] strsToWrite, + SecureString password, out string errorMsg) { // Compute blob size @@ -3335,6 +3394,7 @@ private bool WriteStringArray( name: name, blob: blob, typeName: HashtableType, + password: password, errorMsg: out errorMsg); } @@ -3366,6 +3426,7 @@ private static void ReadStringArray( private bool WriteSecureString( string name, SecureString strToWrite, + SecureString password, out string errorMsg) { if (Utils.GetDataFromSecureString( @@ -3378,6 +3439,7 @@ private bool WriteSecureString( name: name, blob: data, typeName: SecureStringType, + password: password, errorMsg: out errorMsg); } finally @@ -3427,6 +3489,7 @@ private static bool ReadSecureString( private bool WritePSCredential( string name, PSCredential credential, + SecureString password, out string errorMsg) { if (Utils.GetDataFromSecureString( @@ -3466,6 +3529,7 @@ private bool WritePSCredential( name: name, blob: blob, typeName: PSCredentialType, + password: password, errorMsg: out errorMsg); } finally @@ -3539,6 +3603,7 @@ private static bool ReadPSCredential( private bool WriteHashtable( string name, Hashtable hashtable, + SecureString password, out string errorMsg) { // Impose size limit @@ -3586,6 +3651,7 @@ private bool WriteHashtable( if (!WriteStringArray( name: name, strsToWrite: hashTableEntryNames.ToArray(), + password: password, errorMsg: out errorMsg)) { return false; @@ -3597,9 +3663,10 @@ private bool WriteHashtable( { foreach (var entry in entries) { - success = WriteObject( + success = WriteObjectImpl( name: entry.Key, objectToWrite: entry.Value, + password: password, errorMsg: out errorMsg); if (!success) @@ -3620,12 +3687,14 @@ private bool WriteHashtable( { DeleteBlob( name: entry.Key, + password: password, errorMsg: out string _); } // Remove the Hashtable member names. DeleteBlob( name: name, + password: password, errorMsg: out string _); } } @@ -3634,6 +3703,7 @@ private bool WriteHashtable( private bool ReadHashtable( string name, byte[] blob, + SecureString password, out object outHashtable, out string errorMsg) { @@ -3646,8 +3716,9 @@ private bool ReadHashtable( var hashtable = new Hashtable(); foreach (var entryName in entryNames) { - if (ReadObject( + if (ReadObjectImpl( entryName, + password, out object outObject, out errorMsg)) { @@ -3664,11 +3735,13 @@ private bool ReadHashtable( private bool DeleteHashtable( string name, + SecureString password, out string errorMsg) { // Get array of Hashtable secret names. if (!ReadBlob( name, + password, out byte[] blob, out string typeName, out errorMsg)) @@ -3685,12 +3758,14 @@ private bool DeleteHashtable( { DeleteBlob( name: entryName, + password: password, out errorMsg); } // Delete the Hashtable secret names list. DeleteBlob( name: name, + password: password, out errorMsg); return true; From a06eff11537c1ccf088956f44bc968cb0d67990b Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 30 Oct 2020 16:25:22 -0700 Subject: [PATCH 2/7] Second round of preview3 changes --- CHANGELOG.md | 10 ++- build.ps1 | 2 +- help/Reset-SecretStore.md | 38 ++++++++++- help/Set-SecretStorePassword.md | 39 ++++++++++++ help/Update-SecretStorePassword.md | 55 ---------------- src/Microsoft.PowerShell.SecretStore.psd1 | 2 +- src/code/SecretStore.cs | 78 +++++++++++++++++++---- 7 files changed, 153 insertions(+), 71 deletions(-) delete mode 100644 help/Update-SecretStorePassword.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 9449837..a1a1220 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,11 +6,17 @@ ### Changes -- `Set-SecretStoreConfiguration` now has a `-PassThru` parameter to write the store configuration object to the pipeline, and no longer writes the configuration object by default (Issue #25). +- `Set-SecretStoreConfiguration` now has a `-PassThru` parameter to write the store configuration object to the pipeline, and no longer writes the configuration object by default (Issue #29). - A `-PasswordTimeout` value of zero now allows the provided password to be used only once (Issue #30). -- When setting a password, an empty password is no longer accepted (Issue #31) +- When setting a password, an empty password is no longer accepted (Issue #31). + +- `Set-SecretStorePassword` now has a parameter set that takes old/new password arguments, to allow setting password by automation (Issue #26). + +- `Reset-SecretStore` now has a new `-Password` and `-PassThru` parameter. + +- `Reset-SecretStore` will now prompt immediately for a new password, if password authentication is selected and prompt interaction is allowed. ### New Features diff --git a/build.ps1 b/build.ps1 index 24ea1d1..5c50d8b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -40,7 +40,7 @@ param ( [string] $BuildFramework = "netstandard2.0" ) -if ( ! ( Get-Module -ErrorAction SilentlyContinue PSPackageProject) ) { +if ( ! (Get-Module -ErrorAction SilentlyContinue PSPackageProject -ListAvailable)) { Install-Module -Name PSPackageProject -MinimumVersion 0.1.17 -Force } diff --git a/help/Reset-SecretStore.md b/help/Reset-SecretStore.md index 3241f67..61db02e 100644 --- a/help/Reset-SecretStore.md +++ b/help/Reset-SecretStore.md @@ -26,12 +26,17 @@ Default configuration options can be overridden by specifying individual command ### Example 1 ```powershell -PS C:\> Reset-SecretStore +PS C:\> Reset-SecretStore -PassThru WARNING: !!This operation will completely remove all SecretStore module secrets and reset configuration settings to default values!! Reset SecretStore Are you sure you want to erase all secrets in SecretStore and reset configuration settings to default? [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): Y +Creating a new Microsoft.PowerShell.SecretStore vault. A password is required by the current store configuration. +Enter password: +******** +Enter password again for verification: +******** Scope Authentication PasswordTimeout Interaction ----- -------------- --------------- ----------- @@ -77,6 +82,37 @@ Accept pipeline input: False Accept wildcard characters: False ``` +### -Password +Sets the provided Password to the newly reset store. +If the reset store is not configured for password authentication, an error will be returned. + +```yaml +Type: SecureString +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: +Accept pipeline input: False +Accept wildcard characters: False +``` + +### -PassThru +Writes the newly reset store configuration object (SecureStorConfig) to the pipeline. + +```yaml +Type: SwitchParameter +Parameter Sets: (All) +Aliases: + +Required: False +Position: Named +Default value: False +Accept pipeline input: False +Accept wildcard characters: False +``` + ### -PasswordTimeout Configuration option that provides the session password timeout in seconds. Takes an argument whose value determines the session password timeout in seconds. diff --git a/help/Set-SecretStorePassword.md b/help/Set-SecretStorePassword.md index a1a0d0e..7737414 100644 --- a/help/Set-SecretStorePassword.md +++ b/help/Set-SecretStorePassword.md @@ -39,8 +39,47 @@ This example runs the command with no parameter arguments. The user is first prompted for the old password. And then prompted for the new password twice for verification. +### Example 2 +```powershell +PS C:\> Set-SecretStorePassword -NewPassword $newPassword -Password $oldPassword +``` + +This example runs the command passing in both the current store password and the new +password to be set. + ## PARAMETERS +### -NewPassword +New password to be applied to the store. + +```yaml +Type: SecureString +Parameter Sets: ParameterSet +Aliases: + +Required: True +Position: Named +Default value: +Accept pipeline input: True +Accept wildcard characters: False +``` + +### -Password +Existing password needed to unlock the store. +This can be ignored if the store doesn't currently use a password. + +```yaml +Type: SecureString +Parameter Sets: ParameterSet +Aliases: + +Required: False +Position: Named +Default value: +Accept pipeline input: False +Accept wildcard characters: False +``` + ### CommonParameters This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). diff --git a/help/Update-SecretStorePassword.md b/help/Update-SecretStorePassword.md deleted file mode 100644 index 411a42a..0000000 --- a/help/Update-SecretStorePassword.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -external help file: Microsoft.PowerShell.SecretStore.dll-Help.xml -Module Name: Microsoft.PowerShell.SecretStore -online version: -schema: 2.0.0 ---- - -# Update-SecretStorePassword - -## SYNOPSIS -Replaces the current SecretStore password with a new one. - -## SYNTAX - -``` -Update-SecretStorePassword [] -``` - -## DESCRIPTION -This cmdlet updates the password for SecretStore. -It takes no parameters and prompts the user for both the old and new passwords. - -## EXAMPLES - -### Example 1 -```powershell -PS C:\> Update-SecretStorePassword -Old password -Enter password: -******* -New password -Enter password: -******* -Enter password again for verification: -******* -``` - -This example runs the command with no parameter arguments. -The user is first prompted for the old password. -And then prompted for the new password twice for verification. - -## PARAMETERS - -### CommonParameters -This cmdlet supports the common parameters: -Debug, -ErrorAction, -ErrorVariable, -InformationAction, -InformationVariable, -OutVariable, -OutBuffer, -PipelineVariable, -Verbose, -WarningAction, and -WarningVariable. For more information, see [about_CommonParameters](http://go.microsoft.com/fwlink/?LinkID=113216). - -## INPUTS - -### None - -## OUTPUTS - -## NOTES - -## RELATED LINKS diff --git a/src/Microsoft.PowerShell.SecretStore.psd1 b/src/Microsoft.PowerShell.SecretStore.psd1 index 73cc6dc..6f1835d 100644 --- a/src/Microsoft.PowerShell.SecretStore.psd1 +++ b/src/Microsoft.PowerShell.SecretStore.psd1 @@ -8,7 +8,7 @@ RootModule = '.\Microsoft.PowerShell.SecretStore.dll' NestedModules = @('.\Microsoft.PowerShell.SecretStore.Extension') -#RequiredModules = @('Microsoft.PowerShell.SecretManagement') +RequiredModules = @('Microsoft.PowerShell.SecretManagement') # Version number of this module. ModuleVersion = '0.5.3' diff --git a/src/code/SecretStore.cs b/src/code/SecretStore.cs index 59d740f..9531c85 100644 --- a/src/code/SecretStore.cs +++ b/src/code/SecretStore.cs @@ -56,23 +56,55 @@ protected override void EndProcessing() /// /// Updates the local store password to the new password provided. /// - [Cmdlet(VerbsCommon.Set, "SecretStorePassword")] + [Cmdlet(VerbsCommon.Set, "SecretStorePassword", DefaultParameterSetName = NoParameterSet)] public sealed class SetSecretStorePasswordCommand : PSCmdlet { + #region Members + + private const string NoParameterSet = "NoParameterSet"; + private const string ParameterSet = "ParameterSet"; + + #endregion + + #region Parameters + + [Parameter(Position=0, Mandatory=true, ParameterSetName=ParameterSet, ValueFromPipeline=true)] + [ValidateNotNull] + public SecureString NewPassword { get; set; } + + [Parameter(Position=1, ParameterSetName=ParameterSet)] + public SecureString Password { get; set; } + + #endregion + #region Overrides protected override void EndProcessing() { SecureString newPassword; SecureString oldPassword; - oldPassword = Utils.PromptForPassword( - cmdlet: this, - verifyPassword: false, - message: "Old password"); - newPassword = Utils.PromptForPassword( - cmdlet: this, - verifyPassword: true, - message: "New password"); + + switch (ParameterSetName) + { + case NoParameterSet: + oldPassword = Utils.PromptForPassword( + cmdlet: this, + verifyPassword: false, + message: "Old password"); + newPassword = Utils.PromptForPassword( + cmdlet: this, + verifyPassword: true, + message: "New password"); + break; + + case ParameterSet: + oldPassword = Password; + newPassword = NewPassword; + break; + + default: + throw new InvalidOperationException("Unknown parameter set"); + } LocalSecretStore.GetInstance(password: oldPassword).UpdatePassword( newPassword, @@ -238,6 +270,12 @@ public sealed class ResetSecretStoreCommand : PSCmdlet [Parameter] public Interaction Interaction { get; set; } + [Parameter] + public SecureString Password { get; set; } + + [Parameter] + public SwitchParameter PassThru { get; set; } + [Parameter] public SwitchParameter Force { get; set; } @@ -275,11 +313,12 @@ protected override void EndProcessing() } var defaultConfigData = SecureStoreConfig.GetDefault(); + var interaction = MyInvocation.BoundParameters.ContainsKey(nameof(Interaction)) ? Interaction : defaultConfigData.Interaction; var newConfigData = new SecureStoreConfig( scope: MyInvocation.BoundParameters.ContainsKey(nameof(Scope)) ? Scope : defaultConfigData.Scope, authentication: MyInvocation.BoundParameters.ContainsKey(nameof(Authentication)) ? Authentication : defaultConfigData.Authentication, passwordTimeout: MyInvocation.BoundParameters.ContainsKey(nameof(PasswordTimeout)) ? PasswordTimeout : defaultConfigData.PasswordTimeout, - interaction: MyInvocation.BoundParameters.ContainsKey(nameof(Interaction)) ? Interaction : defaultConfigData.Interaction); + interaction: interaction); if (!SecureStoreFile.RemoveStoreFile(out string errorMsg)) { @@ -305,7 +344,24 @@ protected override void EndProcessing() LocalSecretStore.Reset(); - WriteObject(newConfigData); + if (Password != null) + { + LocalSecretStore.GetInstance( + password: Password).UnlockLocalStore( + password: Password, + passwordTimeout: MyInvocation.BoundParameters.ContainsKey(nameof(PasswordTimeout)) ? + (int?)PasswordTimeout : null); + } + else if (interaction == Microsoft.PowerShell.SecretStore.Interaction.Prompt) + { + // Invoke the password prompt. + LocalSecretStore.GetInstance(cmdlet: this); + } + + if (PassThru.IsPresent) + { + WriteObject(newConfigData); + } } #endregion From d66561e26c171ddf65a53ce6ac3892adb0b8aa98 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 2 Nov 2020 14:30:56 -0800 Subject: [PATCH 3/7] Various fixes --- ...rosoft.PowerShell.SecretStore.dll-Help.xml | 146 +++++++++++++++++- src/code/SecretStore.cs | 26 ++-- src/code/Utils.cs | 16 +- 3 files changed, 173 insertions(+), 15 deletions(-) diff --git a/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml b/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml index 121ea79..388b3dc 100644 --- a/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml +++ b/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml @@ -106,6 +106,29 @@ Default configuration options can be overridden by specifying individual command False + + Password + + Sets the provided Password to the newly reset store. If the reset store is not configured for password authentication, an error will be returned. + + SecureString + + SecureString + + + None + + + PassThru + + Writes the newly reset store configuration object (SecureStorConfig) to the pipeline. + + + SwitchParameter + + + False + PasswordTimeout @@ -195,6 +218,30 @@ Default configuration options can be overridden by specifying individual command False + + Password + + Sets the provided Password to the newly reset store. If the reset store is not configured for password authentication, an error will be returned. + + SecureString + + SecureString + + + None + + + PassThru + + Writes the newly reset store configuration object (SecureStorConfig) to the pipeline. + + SwitchParameter + + SwitchParameter + + + False + PasswordTimeout @@ -284,12 +331,17 @@ Default configuration options can be overridden by specifying individual command -------------------------- Example 1 -------------------------- - PS C:\> Reset-SecretStore + PS C:\> Reset-SecretStore -PassThru WARNING: !!This operation will completely remove all SecretStore module secrets and reset configuration settings to default values!! Reset SecretStore Are you sure you want to erase all secrets in SecretStore and reset configuration settings to default? [Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): Y +Creating a new Microsoft.PowerShell.SecretStore vault. A password is required by the current store configuration. +Enter password: +******** +Enter password again for verification: +******** Scope Authentication PasswordTimeout Interaction ----- -------------- --------------- ----------- @@ -339,6 +391,17 @@ CurrentUser Password 900 Prompt False + + PassThru + + When true, will write the current SecretStore configuration to the pipeline. + + + SwitchParameter + + + False + PasswordTimeout @@ -426,6 +489,17 @@ CurrentUser Password 900 Prompt False + + PassThru + + When true, will write the current SecretStore configuration to the pipeline. + + + SwitchParameter + + + False + Confirm @@ -487,6 +561,18 @@ CurrentUser Password 900 Prompt False + + PassThru + + When true, will write the current SecretStore configuration to the pipeline. + + SwitchParameter + + SwitchParameter + + + False + PasswordTimeout @@ -608,9 +694,58 @@ CurrentUser Password 900 Prompt Set-SecretStorePassword + + NewPassword + + New password to be applied to the store. + + SecureString + + SecureString + + + None + + + Password + + Existing password needed to unlock the store. This can be ignored if the store doesn't currently use a password. + + SecureString + + SecureString + + + None + - + + + NewPassword + + New password to be applied to the store. + + SecureString + + SecureString + + + None + + + Password + + Existing password needed to unlock the store. This can be ignored if the store doesn't currently use a password. + + SecureString + + SecureString + + + None + + @@ -643,6 +778,13 @@ Enter password again for verification: This example runs the command with no parameter arguments. The user is first prompted for the old password. And then prompted for the new password twice for verification. + + -------------------------- Example 2 -------------------------- + PS C:\> Set-SecretStorePassword -NewPassword $newPassword -Password $oldPassword + + This example runs the command passing in both the current store password and the new password to be set. + + diff --git a/src/code/SecretStore.cs b/src/code/SecretStore.cs index 9531c85..95cfd3b 100644 --- a/src/code/SecretStore.cs +++ b/src/code/SecretStore.cs @@ -39,11 +39,12 @@ public sealed class UnlockSecretStoreCommand : PSCmdlet protected override void EndProcessing() { - LocalSecretStore.GetInstance( - password: Password).UnlockLocalStore( - password: Password, - passwordTimeout: MyInvocation.BoundParameters.ContainsKey(nameof(PasswordTimeout)) ? - (int?)PasswordTimeout : null); + var password = Utils.CheckPassword(Password); + + LocalSecretStore.GetInstance(password: password).UnlockLocalStore( + password: password, + passwordTimeout: MyInvocation.BoundParameters.ContainsKey(nameof(PasswordTimeout)) ? + (int?)PasswordTimeout : null); } #endregion @@ -98,8 +99,8 @@ protected override void EndProcessing() break; case ParameterSet: - oldPassword = Password; - newPassword = NewPassword; + oldPassword = Utils.CheckPassword(Password); + newPassword = Utils.CheckPassword(NewPassword); break; default: @@ -263,6 +264,9 @@ public sealed class ResetSecretStoreCommand : PSCmdlet [Parameter] public Authenticate Authentication { get; set; } + [Parameter] + public SecureString Password { get; set; } + [Parameter] [ValidateRange(-1, (Int32.MaxValue / 1000))] public int PasswordTimeout { get; set; } @@ -270,9 +274,6 @@ public sealed class ResetSecretStoreCommand : PSCmdlet [Parameter] public Interaction Interaction { get; set; } - [Parameter] - public SecureString Password { get; set; } - [Parameter] public SwitchParameter PassThru { get; set; } @@ -346,9 +347,10 @@ protected override void EndProcessing() if (Password != null) { + var password = Utils.CheckPassword(Password); LocalSecretStore.GetInstance( - password: Password).UnlockLocalStore( - password: Password, + password: password).UnlockLocalStore( + password: password, passwordTimeout: MyInvocation.BoundParameters.ContainsKey(nameof(PasswordTimeout)) ? (int?)PasswordTimeout : null); } diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 78c7760..ab810b1 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -191,7 +191,7 @@ public static SecureString PromptForPassword( "A password is required for Microsoft.PowerShell.SecretStore vault." : message); - var isVerified = !verifyPassword; + var isVerified = false; do { // Initial prompt @@ -216,11 +216,25 @@ public static SecureString PromptForPassword( cmdlet.Host.UI.WriteLine("\nThe two entered passwords do not match. Please re-enter the passwords.\n"); } } + else + { + isVerified = true; + } } while (!isVerified); return password; } + public static SecureString CheckPassword(SecureString password) + { + if (password != null && password.Length == 0) + { + throw new PSInvalidOperationException("A password cannot be empty."); + } + + return password?.Copy() ?? null; + } + #endregion } From deaa05d55175fc253184b7b7bb3310e3a6f65b72 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 2 Nov 2020 14:48:29 -0800 Subject: [PATCH 4/7] Fix no-sign CI --- .ci/templates/sign.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.ci/templates/sign.yml b/.ci/templates/sign.yml index ac7d32d..cdf6e4e 100644 --- a/.ci/templates/sign.yml +++ b/.ci/templates/sign.yml @@ -66,6 +66,7 @@ steps: Copy-Item ${{ parameters.buildOutputPath }}\* ${{ parameters.signOutputPath }}\ -Recurse -Force -Verbose displayName: Prepare output folder timeoutInMinutes: 10 + condition: and(and(and(succeeded(), eq(variables['Build.Reason'], 'Manual')), ne(variables['SkipSigning'], 'True')), ne(variables['SigningServer'], '')) - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 displayName: Sign files From c0b27336789b9680dfc4619b5479d8b68eba814d Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 2 Nov 2020 15:04:28 -0800 Subject: [PATCH 5/7] Fixed issue numbers --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a1a1220..ad3623c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ ### Changes -- `Set-SecretStoreConfiguration` now has a `-PassThru` parameter to write the store configuration object to the pipeline, and no longer writes the configuration object by default (Issue #29). +- `Set-SecretStoreConfiguration` now has a `-PassThru` parameter to write the store configuration object to the pipeline, and no longer writes the configuration object by default (Issue #25). - A `-PasswordTimeout` value of zero now allows the provided password to be used only once (Issue #30). @@ -16,7 +16,7 @@ - `Reset-SecretStore` now has a new `-Password` and `-PassThru` parameter. -- `Reset-SecretStore` will now prompt immediately for a new password, if password authentication is selected and prompt interaction is allowed. +- `Reset-SecretStore` will now prompt immediately for a new password, if password authentication is selected and prompt interaction is allowed (Issue #34) ### New Features From 6ab1211790cdeefbe36e3bec7685576b765317b9 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 3 Nov 2020 09:02:36 -0800 Subject: [PATCH 6/7] Update help/Set-SecretStoreConfiguration.md Co-authored-by: Steve Lee --- help/Set-SecretStoreConfiguration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/help/Set-SecretStoreConfiguration.md b/help/Set-SecretStoreConfiguration.md index 7023db2..3299984 100644 --- a/help/Set-SecretStoreConfiguration.md +++ b/help/Set-SecretStoreConfiguration.md @@ -97,7 +97,7 @@ Accept wildcard characters: False ``` ### -PassThru -When true, will write the current SecretStore configuration to the pipeline. +When used, will write the current SecretStore configuration to the pipeline. ```yaml Type: SwitchParameter From e0fd16917cad4a521c7153a28b9074a84ee33931 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 3 Nov 2020 09:31:29 -0800 Subject: [PATCH 7/7] Review feedback --- help/Reset-SecretStore.md | 2 +- help/Set-SecretStoreConfiguration.md | 4 ++-- ...Microsoft.PowerShell.SecretStore.dll-Help.xml | 16 ++++++++-------- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/help/Reset-SecretStore.md b/help/Reset-SecretStore.md index 61db02e..fba7d8d 100644 --- a/help/Reset-SecretStore.md +++ b/help/Reset-SecretStore.md @@ -67,7 +67,7 @@ Accept wildcard characters: False ``` ### -Force -When true, the user will not be asked to confirm and the SecretStore will be reset without prompting. +When used, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. ```yaml diff --git a/help/Set-SecretStoreConfiguration.md b/help/Set-SecretStoreConfiguration.md index 7023db2..427142c 100644 --- a/help/Set-SecretStoreConfiguration.md +++ b/help/Set-SecretStoreConfiguration.md @@ -81,7 +81,7 @@ Accept wildcard characters: False ``` ### -Force -When true, the user will not be asked to confirm and the SecretStore will be reset without prompting. +When used, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. ```yaml @@ -97,7 +97,7 @@ Accept wildcard characters: False ``` ### -PassThru -When true, will write the current SecretStore configuration to the pipeline. +When used, will write the current SecretStore configuration to the pipeline. ```yaml Type: SwitchParameter diff --git a/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml b/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml index 388b3dc..bc751ad 100644 --- a/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml +++ b/help/en-US/Microsoft.PowerShell.SecretStore.dll-Help.xml @@ -98,7 +98,7 @@ Default configuration options can be overridden by specifying individual command Force - When true, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. + When used, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. SwitchParameter @@ -209,7 +209,7 @@ Default configuration options can be overridden by specifying individual command Force - When true, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. + When used, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. SwitchParameter @@ -383,7 +383,7 @@ CurrentUser Password 900 Prompt Force - When true, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. + When used, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. SwitchParameter @@ -394,7 +394,7 @@ CurrentUser Password 900 Prompt PassThru - When true, will write the current SecretStore configuration to the pipeline. + When used, will write the current SecretStore configuration to the pipeline. SwitchParameter @@ -481,7 +481,7 @@ CurrentUser Password 900 Prompt Force - When true, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. + When used, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. SwitchParameter @@ -492,7 +492,7 @@ CurrentUser Password 900 Prompt PassThru - When true, will write the current SecretStore configuration to the pipeline. + When used, will write the current SecretStore configuration to the pipeline. SwitchParameter @@ -552,7 +552,7 @@ CurrentUser Password 900 Prompt Force - When true, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. + When used, the user will not be asked to confirm and the SecretStore will be reset without prompting. Default value is false, and user will be asked to confirm the operation. SwitchParameter @@ -564,7 +564,7 @@ CurrentUser Password 900 Prompt PassThru - When true, will write the current SecretStore configuration to the pipeline. + When used, will write the current SecretStore configuration to the pipeline. SwitchParameter