From c9c472f1ef01f79c6b3ef4fdec96627f3aa2e280 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Thu, 17 Jun 2021 20:57:48 -0500 Subject: [PATCH 01/20] add support for credential persistence by having a soft dependency on Microsoft.PowerShell.SecretManagement --- .gitignore | 1 + Docs/RegisterPSResourceRepository.md | 11 +- Docs/SetPSResourceRepository.md | 29 ++- src/code/AuthenticationHelper.cs | 194 ++++++++++++++++++ src/code/FindHelper.cs | 23 ++- src/code/InstallHelper.cs | 16 +- src/code/PSRepositoryInfo.cs | 9 +- src/code/RegisterPSResourceRepository.cs | 54 ++++- src/code/RepositorySettings.cs | 120 ++++++++++- src/code/SetPSResourceRepository.cs | 43 +++- test/GetPSResourceRepository.Tests.ps1 | 15 +- test/PSGetTestUtils.psm1 | 19 ++ test/PublishPSResource.Tests.ps1 | 8 +- test/RegisterPSResourceRepository.Tests.ps1 | 68 ++++-- test/SetPSResourceRepository.Tests.ps1 | 113 +++++++++- test/UnregisterPSResourceRepository.Tests.ps1 | 2 +- test/testRepositories.xml | 2 +- test/testRepositoriesWithAuthentication.xml | 7 + 18 files changed, 660 insertions(+), 74 deletions(-) create mode 100644 src/code/AuthenticationHelper.cs create mode 100644 test/testRepositoriesWithAuthentication.xml diff --git a/.gitignore b/.gitignore index 557d7d4e0..465eb2190 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ src/code/obj srcOld/code/bin srcOld/code/obj out +result.pester.xml \ No newline at end of file diff --git a/Docs/RegisterPSResourceRepository.md b/Docs/RegisterPSResourceRepository.md index f5fb2e1c2..48d9d6376 100644 --- a/Docs/RegisterPSResourceRepository.md +++ b/Docs/RegisterPSResourceRepository.md @@ -10,7 +10,7 @@ The Register-PSResourceRepository cmdlet determines which repository will be the ### NameParameterSet (Default) ``` PowerShell -[[-Name] ] [-URL ] [-Priority ] [-Trusted] [-WhatIf] [-Confirm] [] +[[-Name] ] [-URL ] [-Priority ] [-Trusted] [-Authentication] [-WhatIf] [-Confirm] [] ``` ### PSGalleryParameterSet @@ -70,6 +70,15 @@ Type: SwitchParameter Parameter Sets: NameParameterSet, PSGalleryParameterSet ``` +### -Authentication + +Specifies a hashtable of vault and secret names as Authentication information for the repository. + +```yml +Type: Hashtable +Parameter Sets: NameParameterSet +``` + ### -Proxy Specifies a proxy server for the request, rather than a direct connection to the internet resource. diff --git a/Docs/SetPSResourceRepository.md b/Docs/SetPSResourceRepository.md index d363f2a04..7dbda4fd5 100644 --- a/Docs/SetPSResourceRepository.md +++ b/Docs/SetPSResourceRepository.md @@ -13,7 +13,7 @@ The `-URL` for the PSGallery repository, which is pre-defined for this repositor ### NameParameterSet (Default) ``` PowerShell -[[-Name] ] [-URL ] [-Credential ] [-Trusted] [-WhatIf] [-Confirm] [] +[[-Name] ] [-URL ] [-Trusted] [-Priority ] [-Authentication ] [-WhatIf] [-Confirm] [] ``` ### RepositoriesParameterSet @@ -43,15 +43,6 @@ Type: Uri Parameter Sets: NameParameterSet ``` -### -Credential - -Specifies a user account that has rights to find a resource from a specific repository. - -```yml -Type: PSCredential -Parameter Sets: NameParameterSet -``` - ### -Repositories Specifies an array of hashtables containing information on repositories and is used to register multiple repositories at once. @@ -70,6 +61,24 @@ Type: SwitchParameter Parameter Sets: NameParameterSet ``` +### -Priority + +Specifies the priority ranking of the repository, such that repositories with higher ranking priority are searched before a lower ranking priority one, when searching for a repository item across multiple registered repositories. Valid priority values range from 0 to 50, such that a lower numeric value (i.e 10) corresponds to a higher priority ranking than a higher numeric value (i.e 40). + +```yml +Type: int +Parameter Sets: NameParameterSet +``` + +### -Authentication + +Specifies a hashtable of vault and secret names as Authentication information for the repository. + +```yml +Type: Hashtable +Parameter Sets: NameParameterSet +``` + ### -Proxy Specifies a proxy server for the request, rather than a direct connection to the internet resource. diff --git a/src/code/AuthenticationHelper.cs b/src/code/AuthenticationHelper.cs new file mode 100644 index 000000000..aa8ec2e58 --- /dev/null +++ b/src/code/AuthenticationHelper.cs @@ -0,0 +1,194 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Management.Automation; +using System.Management.Automation.Runspaces; + +namespace Microsoft.PowerShell.PowerShellGet.Cmdlets +{ + /// + /// Authentication helper class includes functions to get repository credentials from Microsoft.PowerShell.SecretManagement if provided + /// + internal class AuthenticationHelper + { + internal static readonly string VaultNameAttribute = "VaultName"; + internal static readonly string SecretAttribute = "Secret"; + + private readonly PSCmdlet _cmdletPassedIn; + + private static readonly string SecretManagementModuleName = "Microsoft.PowerShell.SecretManagement"; + + public AuthenticationHelper(PSCmdlet cmdletPassedIn) + { + _cmdletPassedIn = cmdletPassedIn; + } + + public string GetRepositoryAuthenticationPassword(string repositoryName, string vaultName, string secretName) + { + var results = PowerShellInvoker.InvokeScriptWithHost( + cmdlet: _cmdletPassedIn, + script: $@" + param ( + [string] $VaultName, + [string] $SecretName + ) + $module = Microsoft.PowerShell.Core\Import-Module -Name {SecretManagementModuleName} -PassThru + if ($null -eq $module) {{ + return + }} + & $module ""Get-Secret"" -Name $SecretName -Vault $VaultName -AsPlainText + ", + args: new object[] { vaultName, secretName }, + out Exception terminatingError); + + string secretValueInPlainText = (results.Count == 1) ? results[0] : null; + // SecretStore allows empty secret values so only check for null + if (secretValueInPlainText == null) + { + _cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSInvalidOperationException( + message: string.Format(CultureInfo.InvariantCulture, "Unable to read secret {0} from vault {1} for authenticating to PSResourceRepository {2}", secretName, vaultName, repositoryName), + innerException: terminatingError), + "RepositoryAuthenticationCannotGetSecretFromVault", + ErrorCategory.InvalidOperation, + this)); + } + + return secretValueInPlainText; + } + } + + #region PowerShellInvoker + + internal static class PowerShellInvoker + { + #region Members + + private static Runspace _runspace; + + #endregion Members + + #region Methods + + public static Collection InvokeScriptWithHost( + PSCmdlet cmdlet, + string script, + object[] args, + out Exception terminatingError) + { + Collection returnCollection = new Collection(); + terminatingError = null; + + if (_runspace == null || _runspace.RunspaceStateInfo.State != RunspaceState.Opened) + { + if (_runspace != null) + { + _runspace.Dispose(); + } + + var iss = InitialSessionState.CreateDefault2(); + // We are running trusted script. + iss.LanguageMode = PSLanguageMode.FullLanguage; + // Import the current PowerShellGet module. + var modPathObjects = cmdlet.InvokeCommand.InvokeScript( + script: "(Get-Module -Name PowerShellGet).Path"); + string modPath = (modPathObjects.Count > 0 && + modPathObjects[0].BaseObject is string modPathStr) + ? modPathStr : string.Empty; + if (!string.IsNullOrEmpty(modPath)) + { + iss.ImportPSModule(new string[] { modPath }); + } + + try + { + _runspace = RunspaceFactory.CreateRunspace(cmdlet.Host, iss); + _runspace.Open(); + } + catch (Exception ex) + { + terminatingError = ex; + return returnCollection; + } + } + + using (var ps = System.Management.Automation.PowerShell.Create()) + { + ps.Runspace = _runspace; + + var cmd = new Command( + command: script, + isScript: true, + useLocalScope: true); + cmd.MergeMyResults( + myResult: PipelineResultTypes.Error | PipelineResultTypes.Warning | PipelineResultTypes.Verbose | PipelineResultTypes.Debug | PipelineResultTypes.Information, + toResult: PipelineResultTypes.Output); + ps.Commands.AddCommand(cmd); + foreach (var arg in args) + { + ps.Commands.AddArgument(arg); + } + + try + { + // Invoke the script. + var results = ps.Invoke(); + + // Extract expected output types from results pipeline. + foreach (var psItem in results) + { + if (psItem == null || psItem.BaseObject == null) { continue; } + + switch (psItem.BaseObject) + { + case ErrorRecord error: + cmdlet.WriteError(error); + break; + + case WarningRecord warning: + cmdlet.WriteWarning(warning.Message); + break; + + case VerboseRecord verbose: + cmdlet.WriteVerbose(verbose.Message); + break; + + case DebugRecord debug: + cmdlet.WriteDebug(debug.Message); + break; + + case InformationRecord info: + cmdlet.WriteInformation(info); + break; + + case T result: + returnCollection.Add(result); + break; + + case T[] resultArray: + foreach (var item in resultArray) + { + returnCollection.Add(item); + } + break; + } + } + } + catch (Exception ex) + { + terminatingError = ex; + } + } + + return returnCollection; + } + + #endregion Methods + } + + #endregion PowerShellInvoker +} \ No newline at end of file diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index 34d79f289..5dea5d967 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections; using System.Collections.Generic; using System.Data; using Dbg = System.Diagnostics.Debug; @@ -9,6 +10,7 @@ using System.Management.Automation; using System.Net; using System.Net.Http; +using System.Security; using System.Threading; using MoreLinq.Extensions; using Microsoft.PowerShell.PowerShellGet.UtilClasses; @@ -107,7 +109,7 @@ public IEnumerable FindByResourceName( // detect if Script repository needs to be added and/or Module repository needs to be skipped Uri psGalleryScriptsUrl = new Uri("http://www.powershellgallery.com/api/v2/items/psscript/"); - PSRepositoryInfo psGalleryScripts = new PSRepositoryInfo(_psGalleryScriptsRepoName, psGalleryScriptsUrl, repositoriesToSearch[i].Priority, false); + PSRepositoryInfo psGalleryScripts = new PSRepositoryInfo(_psGalleryScriptsRepoName, psGalleryScriptsUrl, repositoriesToSearch[i].Priority, false, null); if (_type == ResourceType.None) { _cmdletPassedIn.WriteVerbose("Null Type provided, so add PSGalleryScripts repository"); @@ -127,7 +129,8 @@ public IEnumerable FindByResourceName( _cmdletPassedIn.WriteVerbose(string.Format("Searching in repository {0}", repositoriesToSearch[i].Name)); foreach (var pkg in SearchFromRepository( repositoryName: repositoriesToSearch[i].Name, - repositoryUrl: repositoriesToSearch[i].Url)) + repositoryUrl: repositoriesToSearch[i].Url, + repositoryAuthentication: repositoriesToSearch[i].Authentication)) { yield return pkg; } @@ -136,7 +139,8 @@ public IEnumerable FindByResourceName( public IEnumerable SearchFromRepository( string repositoryName, - Uri repositoryUrl) + Uri repositoryUrl, + Hashtable repositoryAuthentication) { PackageSearchResource resourceSearch; PackageMetadataResource resourceMetadata; @@ -172,12 +176,25 @@ public IEnumerable SearchFromRepository( // HTTP, HTTPS, FTP Uri schemes (only other Uri schemes allowed by RepositorySettings.Read() API) PackageSource source = new PackageSource(repositoryUrl.ToString()); + + // Explicitly passed in Credential takes precedence over repository Authentication if (_credential != null) { string password = new NetworkCredential(string.Empty, _credential.Password).Password; source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), _credential.UserName, password, true, null); _cmdletPassedIn.WriteVerbose("credential successfully set for repository: " + repositoryName); } + else if (repositoryAuthentication != null) + { + var authHelper = new AuthenticationHelper(_cmdletPassedIn); + string password = authHelper.GetRepositoryAuthenticationPassword( + repositoryName, + repositoryAuthentication[AuthenticationHelper.VaultNameAttribute].ToString(), + repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString()); + + source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString(), password, true, null); + _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repositoryName); + } // GetCoreV3() API is able to handle V2 and V3 repository endpoints var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider); diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index 926f13c8f..d3bb79cd8 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -199,7 +199,7 @@ public void ProcessRepositories(string[] packageNames, string[] repository, bool continue; } - List pkgsInstalled = InstallPackage(pkgsFromRepoToInstall, repoName, repo.Url.AbsoluteUri, credential, isLocalRepo); + List pkgsInstalled = InstallPackage(pkgsFromRepoToInstall, repoName, repo.Url.AbsoluteUri, repo.Authentication, credential, isLocalRepo); foreach (string name in pkgsInstalled) { @@ -249,7 +249,7 @@ public IEnumerable FilterByInstalledPkgs(IEnumerable InstallPackage(IEnumerable pkgsToInstall, string repoName, string repoUrl, PSCredential credential, bool isLocalRepo) + private List InstallPackage(IEnumerable pkgsToInstall, string repoName, string repoUrl, Hashtable repositoryAuthentication, PSCredential credential, bool isLocalRepo) { List pkgsSuccessfullyInstalled = new List(); foreach (PSResourceInfo p in pkgsToInstall) @@ -328,11 +328,23 @@ private List InstallPackage(IEnumerable pkgsToInstall, s /* Download from a non-local repository */ // Set up NuGet API resource for download PackageSource source = new PackageSource(repoUrl); + + // Explicitly passed in Credential takes precedence over repository Authentication if (credential != null) { string password = new NetworkCredential(string.Empty, credential.Password).Password; source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, credential.UserName, password, true, null); } + else if (repositoryAuthentication != null) + { + var authHelper = new AuthenticationHelper(_cmdletPassedIn); + string password = authHelper.GetRepositoryAuthenticationPassword( + repoName, + repositoryAuthentication[AuthenticationHelper.VaultNameAttribute].ToString(), + repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString()); + + source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString(), password, true, null); + } var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider); SourceRepository repository = new SourceRepository(source, provider); diff --git a/src/code/PSRepositoryInfo.cs b/src/code/PSRepositoryInfo.cs index ade75833e..701219ec9 100644 --- a/src/code/PSRepositoryInfo.cs +++ b/src/code/PSRepositoryInfo.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. using System; +using System.Collections; using System.Management.Automation; namespace Microsoft.PowerShell.PowerShellGet.UtilClasses @@ -12,12 +13,13 @@ public sealed class PSRepositoryInfo { #region Constructor - public PSRepositoryInfo(string name, Uri url, int priority, bool trusted) + public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, Hashtable authentication) { Name = name; Url = url; Priority = priority; Trusted = trusted; + Authentication = authentication; } #endregion @@ -44,6 +46,11 @@ public PSRepositoryInfo(string name, Uri url, int priority, bool trusted) [ValidateRange(0, 50)] public int Priority { get; } + /// + /// the Authentication for the repository + /// + public Hashtable Authentication { get; } + #endregion } } diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index be8173488..984085aae 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -63,7 +63,7 @@ class RegisterPSResourceRepository : PSCmdlet /// /// Specifies a hashtable of repositories and is used to register multiple repositories at once. /// - [Parameter(Mandatory = true, ParameterSetName = "RepositoriesParameterSet", ValueFromPipeline = true)] + [Parameter(Mandatory = true, ParameterSetName = RepositoriesParameterSet, ValueFromPipeline = true)] [ValidateNotNullOrEmpty] public Hashtable[] Repositories {get; set;} @@ -85,6 +85,13 @@ class RegisterPSResourceRepository : PSCmdlet [ValidateRange(0, 50)] public int Priority { get; set; } = defaultPriority; + /// + /// Specifies a hashtable of vault and secret names as Authentication information for the repository. + /// + [Parameter(ParameterSetName = NameParameterSet)] + [ValidateNotNullOrEmpty] + public Hashtable Authentication {get; set;} + /// /// Specifies a proxy server for the request, rather than a direct connection to the internet resource. /// @@ -138,7 +145,7 @@ protected override void ProcessRecord() try { - items.Add(NameParameterSetHelper(Name, _url, Priority, Trusted)); + items.Add(NameParameterSetHelper(Name, _url, Priority, Trusted, Authentication)); } catch (Exception e) { @@ -195,7 +202,7 @@ protected override void ProcessRecord() } } - private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted) + private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) { // remove trailing and leading whitespaces, and if Name is just whitespace Name should become null now and be caught by following condition repoName = repoName.Trim(' '); @@ -209,16 +216,25 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl throw new ArgumentException("Invalid url, must be one of the following Uri schemes: HTTPS, HTTP, FTP, File Based"); } + if (repoAuthentication != null) + { + if (!repoAuthentication.ContainsKey(AuthenticationHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.VaultNameAttribute].ToString()) + || !repoAuthentication.ContainsKey(AuthenticationHelper.SecretAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.SecretAttribute].ToString())) + { + throw new ArgumentException($"Invalid Authentication, must include {AuthenticationHelper.VaultNameAttribute} and {AuthenticationHelper.SecretAttribute} key/(non-empty) value pairs"); + } + } + WriteVerbose("All required values to add to repository provided, calling internal Add() API now"); if (!ShouldProcess(repoName, "Register repository to repository store")) { return null; } - return RepositorySettings.Add(repoName, repoUrl, repoPriority, repoTrusted); + return RepositorySettings.Add(repoName, repoUrl, repoPriority, repoTrusted, repoAuthentication); } - private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted) + private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) { if (repoName.Equals("PSGallery", StringComparison.OrdinalIgnoreCase)) { @@ -226,14 +242,14 @@ private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, in throw new ArgumentException("Cannot register PSGallery with -Name parameter. Try: Register-PSResourceRepository -PSGallery"); } - return AddToRepositoryStoreHelper(repoName, repoUrl, repoPriority, repoTrusted); + return AddToRepositoryStoreHelper(repoName, repoUrl, repoPriority, repoTrusted, repoAuthentication); } private PSRepositoryInfo PSGalleryParameterSetHelper(int repoPriority, bool repoTrusted) { Uri psGalleryUri = new Uri(PSGalleryRepoURL); WriteVerbose("(PSGallerySet) internal name and uri values for Add() API are hardcoded and validated, priority and trusted values, if passed in, also validated"); - return AddToRepositoryStoreHelper(PSGalleryRepoName, psGalleryUri, repoPriority, repoTrusted); + return AddToRepositoryStoreHelper(PSGalleryRepoName, psGalleryUri, repoPriority, repoTrusted, null); } private List RepositoriesParameterSetHelper() @@ -243,11 +259,11 @@ private List RepositoriesParameterSetHelper() { if (repo.ContainsKey(PSGalleryRepoName)) { - if (repo.ContainsKey("Name") || repo.ContainsKey("Url")) + if (repo.ContainsKey("Name") || repo.ContainsKey("Url") || repo.ContainsKey("Authentication")) { WriteError(new ErrorRecord( - new PSInvalidOperationException("Repository hashtable cannot contain PSGallery key with -Name and/or -URL key value pairs"), - "NotProvideNameUrlForPSGalleryRepositoriesParameterSetRegistration", + new PSInvalidOperationException("Repository hashtable cannot contain PSGallery key with -Name, -URL and/or -Authentication key value pairs"), + "NotProvideNameUrlAuthForPSGalleryRepositoriesParameterSetRegistration", ErrorCategory.InvalidArgument, this)); continue; @@ -323,13 +339,29 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) return null; } + Hashtable repoAuthentication = repo["Authentication"] as Hashtable; + if (repoAuthentication != null) + { + if (!repoAuthentication.ContainsKey(AuthenticationHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.VaultNameAttribute].ToString()) + || !repoAuthentication.ContainsKey(AuthenticationHelper.SecretAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.SecretAttribute].ToString())) + { + WriteError(new ErrorRecord( + new PSInvalidOperationException($"Invalid Authentication, must include {AuthenticationHelper.VaultNameAttribute} and {AuthenticationHelper.SecretAttribute} key/(non-empty) value pairs"), + "InvalidAuthentication", + ErrorCategory.InvalidArgument, + this)); + return null; + } + } + try { WriteVerbose(String.Format("(RepositoriesParameterSet): on repo: {0}. Registers Name based repository", repo["Name"])); return NameParameterSetHelper(repo["Name"].ToString(), repoURL, repo.ContainsKey("Priority") ? Convert.ToInt32(repo["Priority"].ToString()) : defaultPriority, - repo.ContainsKey("Trusted") ? Convert.ToBoolean(repo["Trusted"].ToString()) : defaultTrusted); + repo.ContainsKey("Trusted") ? Convert.ToBoolean(repo["Trusted"].ToString()) : defaultTrusted, + repoAuthentication); } catch (Exception e) { diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs index 529e56186..b16b6e428 100644 --- a/src/code/RepositorySettings.cs +++ b/src/code/RepositorySettings.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -19,7 +20,7 @@ namespace Microsoft.PowerShell.PowerShellGet.UtilClasses /// internal static class RepositorySettings - { + { /// /// File name for a user's repository store file is 'PSResourceRepository.xml' /// The repository store file's location is currently only at '%LOCALAPPDATA%\PowerShellGet' for the user account. @@ -32,6 +33,9 @@ internal static class RepositorySettings private static readonly string RepositoryPath = Path.Combine(Environment.GetFolderPath(SpecialFolder.LocalApplicationData), "PowerShellGet"); private static readonly string FullRepositoryPath = Path.Combine(RepositoryPath, RepositoryFileName); + private static readonly string VaultNameAttribute = "VaultName"; + private static readonly string SecretAttribute = "Secret"; + /// /// Check if repository store xml file exists, if not then create /// @@ -58,7 +62,7 @@ public static void CheckRepositoryStore() // Add PSGallery to the newly created store Uri psGalleryUri = new Uri(PSGalleryRepoURL); - Add(PSGalleryRepoName, psGalleryUri, defaultPriority, defaultTrusted); + Add(PSGalleryRepoName, psGalleryUri, defaultPriority, defaultTrusted, null); } // Open file (which should exist now), if cannot/is corrupted then throw error @@ -77,7 +81,7 @@ public static void CheckRepositoryStore() /// Returns: PSRepositoryInfo containing information about the repository just added to the repository store /// /// - public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriority, bool repoTrusted) + public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) { Dbg.Assert(!string.IsNullOrEmpty(repoName), "Repository name cannot be null or empty"); Dbg.Assert(!string.IsNullOrEmpty(repoURL.ToString()), "Repository URL cannot be null or empty"); @@ -104,6 +108,16 @@ public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriorit new XAttribute("Trusted", repoTrusted) ); + if(repoAuthentication != null) { + Dbg.Assert(repoAuthentication.ContainsKey(VaultNameAttribute), "Authentication has to contain Vault Name"); + Dbg.Assert(!string.IsNullOrEmpty(repoAuthentication[VaultNameAttribute].ToString()), "Vault Name cannot be null or empty"); + Dbg.Assert(repoAuthentication.ContainsKey(SecretAttribute), "Authentication has to contain Secret"); + Dbg.Assert(!string.IsNullOrEmpty(repoAuthentication[SecretAttribute].ToString()), "Secret cannot be null or empty"); + + newElement.Add(new XAttribute(VaultNameAttribute, repoAuthentication[VaultNameAttribute])); + newElement.Add(new XAttribute(SecretAttribute, repoAuthentication[SecretAttribute])); + } + root.Add(newElement); // Close the file @@ -114,14 +128,14 @@ public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriorit throw new PSInvalidOperationException(String.Format("Adding to repository store failed: {0}", e.Message)); } - return new PSRepositoryInfo(repoName, repoURL, repoPriority, repoTrusted); + return new PSRepositoryInfo(repoName, repoURL, repoPriority, repoTrusted, repoAuthentication); } /// /// Updates a repository name, URL, priority, or installation policy /// Returns: void /// - public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPriority, bool? repoTrusted) + public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPriority, bool? repoTrusted, Hashtable repoAuthentication) { Dbg.Assert(!string.IsNullOrEmpty(repoName), "Repository name cannot be null or empty"); @@ -161,16 +175,49 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio node.Attribute("Trusted").Value = repoTrusted.ToString(); } + // A null Authentication value passed in signifies that Authentication information was not attempted to be set. + // So only set VaultName and Secret attributes if non-null value passed in for repoAuthentication + if (repoAuthentication != null) + { + Dbg.Assert(repoAuthentication.ContainsKey(VaultNameAttribute), "Authentication has to contain Vault Name"); + Dbg.Assert(!string.IsNullOrEmpty(repoAuthentication[VaultNameAttribute].ToString()), "Vault Name cannot be null or empty"); + Dbg.Assert(repoAuthentication.ContainsKey(SecretAttribute), "Authentication has to contain Secret"); + Dbg.Assert(!string.IsNullOrEmpty(repoAuthentication[SecretAttribute].ToString()), "Secret cannot be null or empty"); + + if (node.Attribute(VaultNameAttribute) == null) { + node.Add(new XAttribute(VaultNameAttribute, repoAuthentication[VaultNameAttribute])); + } + else { + node.Attribute(VaultNameAttribute).Value = repoAuthentication[VaultNameAttribute].ToString(); + } + + if (node.Attribute(SecretAttribute) == null) { + node.Add(new XAttribute(SecretAttribute, repoAuthentication[SecretAttribute])); + } + else { + node.Attribute(SecretAttribute).Value = repoAuthentication[SecretAttribute].ToString(); + } + } + // Create Uri from node Url attribute to create PSRepositoryInfo item to return. if (!Uri.TryCreate(node.Attribute("Url").Value, UriKind.Absolute, out Uri thisUrl)) { throw new PSInvalidOperationException(String.Format("Unable to read incorrectly formatted URL for repo {0}", repoName)); } + // Create Authentication based on new values or whether it was empty to begin with + Hashtable thisAuthentication = !string.IsNullOrEmpty(node.Attribute(VaultNameAttribute)?.Value) && !string.IsNullOrEmpty(node.Attribute(SecretAttribute)?.Value) + ? new Hashtable() { + { VaultNameAttribute, node.Attribute(VaultNameAttribute).Value }, + { SecretAttribute, node.Attribute(SecretAttribute).Value } + } + : null; + updatedRepo = new PSRepositoryInfo(repoName, thisUrl, Int32.Parse(node.Attribute("Priority").Value), - Boolean.Parse(node.Attribute("Trusted").Value)); + Boolean.Parse(node.Attribute("Trusted").Value), + thisAuthentication); // Close the file root.Save(FullRepositoryPath); @@ -258,15 +305,42 @@ public static List Read(string[] repoNames, out string[] error continue; } + Hashtable thisAuthentication = null; + string authErrorMessage = $"Repository {repo.Attribute("Name")} has invalid Authentication information. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; + // both keys present + if (repo.Attribute(VaultNameAttribute) != null && repo.Attribute(SecretAttribute) != null) { + // both values non-empty + // = valid authentication + if (!string.IsNullOrEmpty(repo.Attribute(VaultNameAttribute).Value) && !string.IsNullOrEmpty(repo.Attribute(SecretAttribute).Value)) { + thisAuthentication = new Hashtable() { + { VaultNameAttribute, repo.Attribute(VaultNameAttribute).Value }, + { SecretAttribute, repo.Attribute(SecretAttribute).Value } + }; + } + else { + tempErrorList.Add(authErrorMessage); + continue; + } + } + // both keys are missing + else if (repo.Attribute(VaultNameAttribute) == null && repo.Attribute(SecretAttribute) == null) { + // = valid authentication, do nothing + } + // one of the keys is missing + else { + tempErrorList.Add(authErrorMessage); + continue; + } + PSRepositoryInfo currentRepoItem = new PSRepositoryInfo(repo.Attribute("Name").Value, thisUrl, Int32.Parse(repo.Attribute("Priority").Value), - Boolean.Parse(repo.Attribute("Trusted").Value)); + Boolean.Parse(repo.Attribute("Trusted").Value), + thisAuthentication); foundRepos.Add(currentRepoItem); } } - else { foreach (string repo in repoNames) @@ -284,10 +358,38 @@ public static List Read(string[] repoNames, out string[] error continue; } + Hashtable thisAuthentication = null; + string authErrorMessage = $"Repository {node.Attribute("Name")} has invalid Authentication information. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; + // both keys present + if (node.Attribute(VaultNameAttribute) != null && node.Attribute(SecretAttribute) != null) { + // both values non-empty + // = valid authentication + if (!string.IsNullOrEmpty(node.Attribute(VaultNameAttribute).Value) && !string.IsNullOrEmpty(node.Attribute(SecretAttribute).Value)) { + thisAuthentication = new Hashtable() { + { VaultNameAttribute, node.Attribute(VaultNameAttribute).Value }, + { SecretAttribute, node.Attribute(SecretAttribute).Value } + }; + } + else { + tempErrorList.Add(authErrorMessage); + continue; + } + } + // both keys are missing + else if (node.Attribute(VaultNameAttribute) == null && node.Attribute(SecretAttribute) == null) { + // = valid authentication, do nothing + } + // one of the keys is missing + else { + tempErrorList.Add(authErrorMessage); + continue; + } + PSRepositoryInfo currentRepoItem = new PSRepositoryInfo(node.Attribute("Name").Value, thisUrl, Int32.Parse(node.Attribute("Priority").Value), - Boolean.Parse(node.Attribute("Trusted").Value)); + Boolean.Parse(node.Attribute("Trusted").Value), + thisAuthentication); foundRepos.Add(currentRepoItem); } diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs index 284a27e7e..c591c0bd2 100644 --- a/src/code/SetPSResourceRepository.cs +++ b/src/code/SetPSResourceRepository.cs @@ -28,6 +28,7 @@ class SetPSResourceRepository : PSCmdlet private const string RepositoriesParameterSet = "RepositoriesParameterSet"; private const int DefaultPriority = -1; private Uri _url; + #endregion #region Parameters @@ -85,7 +86,14 @@ public SwitchParameter Trusted public int Priority { get; set; } = DefaultPriority; /// - /// When specified, displays the succcessfully registered repository and its information + /// Specifies a hashtable of vault and secret names as Authentication information for the repository. + /// + [Parameter(ParameterSetName = NameParameterSet)] + [ValidateNotNullOrEmpty] + public Hashtable Authentication {get; set;} + + /// + /// When specified, displays the successfully registered repository and its information /// [Parameter] public SwitchParameter PassThru { get; set; } @@ -116,7 +124,7 @@ protected override void ProcessRecord() case NameParameterSet: try { - items.Add(UpdateRepositoryStoreHelper(Name, _url, Priority, Trusted)); + items.Add(UpdateRepositoryStoreHelper(Name, _url, Priority, Trusted, Authentication)); } catch (Exception e) { @@ -157,7 +165,7 @@ protected override void ProcessRecord() } } - private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted) + private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) { if (repoUrl != null && !(repoUrl.Scheme == Uri.UriSchemeHttp || repoUrl.Scheme == Uri.UriSchemeHttps || repoUrl.Scheme == Uri.UriSchemeFtp || repoUrl.Scheme == Uri.UriSchemeFile)) { @@ -175,17 +183,32 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr // check PSGallery URL is not trying to be set if (repoName.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) && repoUrl != null) { - throw new ArgumentException("The PSGallery repository has a pre-defined URL. Setting the -URL parmeter for this repository is not allowed, instead try running 'Register-PSResourceRepository -PSGallery'."); + throw new ArgumentException("The PSGallery repository has a pre-defined URL. Setting the -URL parameter for this repository is not allowed, instead try running 'Register-PSResourceRepository -PSGallery'."); + } + + // check PSGallery Authentication is not trying to be set + if (repoName.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) && repoAuthentication != null) + { + throw new ArgumentException("The PSGallery repository does not require authentication. Setting the -Authentication parameter for this repository is not allowed, instead try running 'Register-PSResourceRepository -PSGallery'."); } // determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable) bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?(); - // determine if either 1 of 3 values are attempting to be set: URL, Priority, Trusted. + if (repoAuthentication != null) + { + if (!repoAuthentication.ContainsKey(AuthenticationHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.VaultNameAttribute].ToString()) + || !repoAuthentication.ContainsKey(AuthenticationHelper.SecretAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.SecretAttribute].ToString())) + { + throw new ArgumentException($"Invalid Authentication, must include {AuthenticationHelper.VaultNameAttribute} and {AuthenticationHelper.SecretAttribute} key/(non-empty) value pairs"); + } + } + + // determine if either 1 of 4 values are attempting to be set: URL, Priority, Trusted, Authentication. // if none are (i.e only Name parameter was provided, write error) - if(repoUrl == null && repoPriority == DefaultPriority && _trustedNullable == null) + if(repoUrl == null && repoPriority == DefaultPriority && _trustedNullable == null && repoAuthentication == null) { - throw new ArgumentException("Either URL, Priority or Trusted parameters must be requested to be set"); + throw new ArgumentException("Either URL, Priority, Trusted or Authentication parameters must be requested to be set"); } WriteVerbose("All required values to set repository provided, calling internal Update() API now"); @@ -193,7 +216,7 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr { return null; } - return RepositorySettings.Update(repoName, repoUrl, repoPriority, _trustedNullable); + return RepositorySettings.Update(repoName, repoUrl, repoPriority, _trustedNullable, repoAuthentication); } private List RepositoriesParameterSetHelper() @@ -254,12 +277,14 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) repoTrusted = (bool) repo["Trusted"]; isSet = true; } + try { return UpdateRepositoryStoreHelper(repo["Name"].ToString(), repoURL, repo.ContainsKey("Priority") ? Convert.ToInt32(repo["Priority"].ToString()) : DefaultPriority, - repoTrusted); + repoTrusted, + repo["Authentication"] as Hashtable); } catch (Exception e) { diff --git a/test/GetPSResourceRepository.Tests.ps1 b/test/GetPSResourceRepository.Tests.ps1 index aa9d4773c..486c9cd87 100644 --- a/test/GetPSResourceRepository.Tests.ps1 +++ b/test/GetPSResourceRepository.Tests.ps1 @@ -3,7 +3,7 @@ Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force -Describe "Test Register-PSResourceRepository" { +Describe "Test Get-PSResourceRepository" { BeforeEach { Get-NewPSResourceRepositoryFile $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1" @@ -83,6 +83,19 @@ Describe "Test Register-PSResourceRepository" { } } + It "given invalid and valid Authentication information, get valid ones and write error for non valid ones" { + Get-NewPSResourceRepositoryFileWithAuthentication + + $res = Get-PSResourceRepository -Name "localtestrepo*" -ErrorVariable err -ErrorAction SilentlyContinue + $err.Count | Should -Not -Be 0 + $err[0].FullyQualifiedErrorId | Should -BeExactly "ErrorGettingSpecifiedRepo,Microsoft.PowerShell.PowerShellGet.Cmdlets.GetPSResourceRepository" + + # should have successfully got the other valid/registered repositories with no error + foreach ($entry in $res) { + $entry.Name | Should -BeIn "localtestrepo1","localtestrepo2" + } + } + It "throw error and get no repositories when provided null Name" { # $errorMsg = "Cannot validate argument on parameter 'Name'. The argument is null or empty. Provide an argument that is not null or empty, and then try the command again." {Get-PSResourceRepository -Name $null -ErrorAction Stop} | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.PowerShellGet.Cmdlets.GetPSResourceRepository" diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1 index 4cae5c5ec..f7c9249ad 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -205,6 +205,25 @@ function Get-RevertPSResourceRepositoryFile { } } +function Get-NewPSResourceRepositoryFileWithAuthentication { + # register our own repositories with desired priority + $powerShellGetPath = Join-Path -Path ([Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData)) -ChildPath "PowerShellGet" + $originalXmlFilePath = Join-Path -Path $powerShellGetPath -ChildPath "PSResourceRepository.xml" + $tempXmlFilePath = Join-Path -Path $powerShellGetPath -ChildPath "temp.xml" + + if (Test-Path -Path $originalXmlFilePath) { + Copy-Item -Path $originalXmlFilePath -Destination $tempXmlFilePath + Remove-Item -Path $originalXmlFilePath -Force -ErrorAction Ignore + } + + if (! (Test-Path -Path $powerShellGetPath)) { + $null = New-Item -Path $powerShellGetPath -ItemType Directory -Verbose + } + + $fileToCopy = Join-Path -Path $PSScriptRoot -ChildPath "testRepositoriesWithAuthentication.xml" + Copy-Item -Path $fileToCopy -Destination $originalXmlFilePath -Force -Verbose +} + function Register-LocalRepos { $repoURLAddress = Join-Path -Path $TestDrive -ChildPath "testdir" $null = New-Item $repoURLAddress -ItemType Directory -Force diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index 2a49a1799..2fc9eadc4 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -11,14 +11,14 @@ Describe "Test Publish-PSResource" { $tmpRepoPath = Join-Path -Path $TestDrive -ChildPath "tmpRepoPath" New-Item $tmpRepoPath -Itemtype directory -Force $testRepository = "testRepository" - Register-PSResourceRepository -Name $testRepository -URL $tmpRepoPath -Priority 1 -ErrorAction SilentlyContinue - $script:repositoryPath = (get-psresourcerepository "testRepository").Url.AbsolutePath + Register-PSResourceRepository -Name $testRepository -URL $tmpRepoPath -Priority 1 -ErrorAction SilentlyContinue + $script:repositoryPath = [IO.Path]::GetFullPath((get-psresourcerepository "testRepository").Url.AbsolutePath) $tmpRepoPath2 = Join-Path -Path $TestDrive -ChildPath "tmpRepoPath2" New-Item $tmpRepoPath2 -Itemtype directory -Force $testRepository2 = "testRepository2" Register-PSResourceRepository -Name $testRepository2 -URL $tmpRepoPath2 -ErrorAction SilentlyContinue - $script:repositoryPath2 = (get-psresourcerepository "testRepository2").Url.AbsolutePath + $script:repositoryPath2 = [IO.Path]::GetFullPath((get-psresourcerepository "testRepository2").Url.AbsolutePath) # Create module $script:tmpModulesPath = Join-Path -Path $TestDrive -ChildPath "tmpModulesPath" @@ -122,7 +122,7 @@ Describe "Test Publish-PSResource" { Publish-PSResource -Path $script:PublishModuleBase -SkipDependenciesCheck $expectedPath = Join-Path -Path $script:repositoryPath -ChildPath "$script:PublishModuleName.$version.nupkg" - (Get-ChildItem $script:repositoryPath).FullName | select-object -Last 1 | Should -Be $expectedPath + (Get-ChildItem $script:repositoryPath).FullName | select-object -Last 1 | Should -Be $expectedPath } <# The following tests are related to passing in parameters to customize a nuspec. diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index fd4204774..a2518ee8d 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -11,7 +11,8 @@ Describe "Test Register-PSResourceRepository" { $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1" $tmpDir2Path = Join-Path -Path $TestDrive -ChildPath "tmpDir2" $tmpDir3Path = Join-Path -Path $TestDrive -ChildPath "tmpDir3" - $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path) + $tmpDir4Path = Join-Path -Path $TestDrive -ChildPath "tmpDir4" + $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path, $tmpDir4Path) Get-NewTestDirs($tmpDirPaths) $relativeCurrentPath = Get-Location @@ -21,7 +22,8 @@ Describe "Test Register-PSResourceRepository" { $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1" $tmpDir2Path = Join-Path -Path $TestDrive -ChildPath "tmpDir2" $tmpDir3Path = Join-Path -Path $TestDrive -ChildPath "tmpDir3" - $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path) + $tmpDir4Path = Join-Path -Path $TestDrive -ChildPath "tmpDir4" + $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path, $tmpDir4Path) Get-RemoveTestDirs($tmpDirPaths) } @@ -49,6 +51,16 @@ Describe "Test Register-PSResourceRepository" { $res.Priority | Should -Be 20 } + It "register repository given Name, URL, Trusted, Priority, Authentication (NameParameterSet)" { + $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -Authentication @{VaultName = "testvault"; Secret = "testsecret"} -PassThru + $res.Name | Should -Be "testRepository" + $res.URL | Should -Contain $tmpDir1Path + $res.Trusted | Should -Be True + $res.Priority | Should -Be 20 + $res.Authentication["VaultName"] | Should -Be "testvault" + $res.Authentication["Secret"] | Should -Be "testsecret" + } + It "register repository with PSGallery parameter (PSGalleryParameterSet)" { Unregister-PSResourceRepository -Name $PSGalleryName $res = Register-PSResourceRepository -PSGallery -PassThru @@ -80,7 +92,8 @@ Describe "Test Register-PSResourceRepository" { $hashtable1 = @{Name = "testRepository"; URL = $tmpDir1Path} $hashtable2 = @{Name = "testRepository2"; URL = $tmpDir2Path; Trusted = $True} $hashtable3 = @{Name = "testRepository3"; URL = $tmpDir3Path; Trusted = $True; Priority = 20} - $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3 + $hashtable4 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; Authentication = @{VaultName = "testvault"; Secret = "testsecret"}} + $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 Register-PSResourceRepository -Repositories $arrayOfHashtables $res = Get-PSResourceRepository -Name "testRepository" @@ -97,6 +110,13 @@ Describe "Test Register-PSResourceRepository" { $res3.URL.LocalPath | Should -Contain $tmpDir3Path $res3.Trusted | Should -Be True $res3.Priority | Should -Be 20 + + $res4 = Get-PSResourceRepository -Name "testRepository4" + $res4.URL | Should -Contain $tmpDir4Path + $res4.Trusted | Should -Be True + $res4.Priority | Should -Be 30 + $res4.Authentication["VaultName"] | Should -Be "testvault" + $res4.Authentication["Secret"] | Should -Be "testsecret" } It "register repositories with Repositories parameter, psgallery style repository (RepositoriesParameterSet)" { @@ -115,7 +135,8 @@ Describe "Test Register-PSResourceRepository" { $hashtable2 = @{Name = "testRepository"; URL = $tmpDir1Path} $hashtable3 = @{Name = "testRepository2"; URL = $tmpDir2Path; Trusted = $True} $hashtable4 = @{Name = "testRepository3"; URL = $tmpDir3Path; Trusted = $True; Priority = 20} - $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 + $hashtable5 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; Authentication = @{VaultName = "testvault"; Secret = "testsecret"}} + $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4, $hashtable5 Register-PSResourceRepository -Repositories $arrayOfHashtables @@ -138,6 +159,13 @@ Describe "Test Register-PSResourceRepository" { $res4.URL.LocalPath | Should -Contain $tmpDir3Path $res4.Trusted | Should -Be True $res4.Priority | Should -Be 20 + + $res5 = Get-PSResourceRepository -Name "testRepository4" + $res5.URL | Should -Contain $tmpDir4Path + $res5.Trusted | Should -Be True + $res5.Priority | Should -Be 30 + $res5.Authentication["VaultName"] | Should -Be "testvault" + $res5.Authentication["Secret"] | Should -Be "testsecret" } It "not register repository when Name is provided but URL is not" { @@ -148,7 +176,7 @@ Describe "Test Register-PSResourceRepository" { {Register-PSResourceRepository -Name "" -URL $tmpDir1Path -ErrorAction Stop} | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } - It "not register rpeository when Name is null but URL is provided" { + It "not register repository when Name is null but URL is provided" { {Register-PSResourceRepository -Name $null -URL $tmpDir1Path -ErrorAction Stop} | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } @@ -156,18 +184,30 @@ Describe "Test Register-PSResourceRepository" { {Register-PSResourceRepository -Name " " -URL $tmpDir1Path -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } + It "not register if Authentication is missing VaultName or Secret" { + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + } + + It "not register if Authentication values are empty" { + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{VaultName = ""; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + } + It "not register PSGallery with NameParameterSet" { {Register-PSResourceRepository -Name $PSGalleryName -URL $PSGalleryURL -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } # this error message comes from the parameter cmdlet tags (earliest point of detection) - It "not register PSGallery when PSGallery parameter provided with Name or URL" { + It "not register PSGallery when PSGallery parameter provided with Name, URL or Authentication" { {Register-PSResourceRepository -PSGallery -Name $PSGalleryName -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" {Register-PSResourceRepository -PSGallery -URL $PSGalleryURL -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -PSGallery -Authentication @{VaultName = "testvault"; Secret = "testsecret"} -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } $testCases = @{Type = "Name key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; Name=$PSGalleryName}}, - @{Type = "URL key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; URL=$PSGalleryURL}} + @{Type = "URL key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; URL=$PSGalleryURL}}, + @{Type = "Authentication key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; Authentication = @{VaultName = "test"; Secret = "test"}}} It "not register incorrectly formatted PSGallery type repo among correct ones when incorrect type is " -TestCases $testCases { param($Type, $IncorrectHashTable) @@ -180,7 +220,7 @@ Describe "Test Register-PSResourceRepository" { Unregister-PSResourceRepository -Name "PSGallery" Register-PSResourceRepository -Repositories $arrayOfHashtables -ErrorVariable err -ErrorAction SilentlyContinue $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "NotProvideNameUrlForPSGalleryRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + $err[0].FullyQualifiedErrorId | Should -BeExactly "NotProvideNameUrlAuthForPSGalleryRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" $res = Get-PSResourceRepository -Name "testRepository" $res.Name | Should -Be "testRepository" @@ -192,10 +232,14 @@ Describe "Test Register-PSResourceRepository" { $res3.Name | Should -Be "testRepository3" } - $testCases2 = @{Type = "-Name is not specified"; IncorrectHashTable = @{URL = $tmpDir1Path}; ErrorId = "NullNameForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-Name is PSGallery"; IncorrectHashTable = @{Name = "PSGallery"; URL = $tmpDir1Path}; ErrorId = "PSGalleryProvidedAsNameRepoPSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-URL not specified"; IncorrectHashTable = @{Name = "testRepository"}; ErrorId = "NullURLForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-URL is not valid scheme"; IncorrectHashTable = @{Name = "testRepository"; URL="www.google.com"}; ErrorId = "InvalidUrl,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} + $testCases2 = @{Type = "-Name is not specified"; IncorrectHashTable = @{URL = $tmpDir1Path}; ErrorId = "NullNameForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-Name is PSGallery"; IncorrectHashTable = @{Name = "PSGallery"; URL = $tmpDir1Path}; ErrorId = "PSGalleryProvidedAsNameRepoPSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-URL not specified"; IncorrectHashTable = @{Name = "testRepository"}; ErrorId = "NullURLForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-URL is not valid scheme"; IncorrectHashTable = @{Name = "testRepository"; URL="www.google.com"}; ErrorId = "InvalidUrl,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-Authentication is missing VaultName"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{Secret = "test"}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-Authentication is missing Secret"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{VaultName = "test"}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-Authentication-VaultName value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{VaultName = ""; Secret = "test"}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-Authentication-Secret value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{VaultName = "test"; Secret = ""}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} It "not register incorrectly formatted Name type repo among correct ones when incorrect type is " -TestCases $testCases2 { param($Type, $IncorrectHashTable, $ErrorId) diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index c302c3d79..84e1a639e 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -11,7 +11,8 @@ Describe "Test Set-PSResourceRepository" { $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1" $tmpDir2Path = Join-Path -Path $TestDrive -ChildPath "tmpDir2" $tmpDir3Path = Join-Path -Path $TestDrive -ChildPath "tmpDir3" - $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path) + $tmpDir4Path = Join-Path -Path $TestDrive -ChildPath "tmpDir4" + $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path, $tmpDir4Path) Get-NewTestDirs($tmpDirPaths) $relativeCurrentPath = Get-Location @@ -21,7 +22,8 @@ Describe "Test Set-PSResourceRepository" { $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1" $tmpDir2Path = Join-Path -Path $TestDrive -ChildPath "tmpDir2" $tmpDir3Path = Join-Path -Path $TestDrive -ChildPath "tmpDir3" - $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path) + $tmpDir4Path = Join-Path -Path $TestDrive -ChildPath "tmpDir4" + $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path, $tmpDir4Path) Get-RemoveTestDirs($tmpDirPaths) } @@ -33,6 +35,7 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir2Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False + $res.Authentication | Should -BeNullOrEmpty } It "set repository given Name and Priority parameters" { @@ -43,6 +46,7 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 25 $res.Trusted | Should -Be False + $res.Authentication | Should -BeNullOrEmpty } It "set repository given Name and Trusted parameters" { @@ -53,6 +57,19 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be True + $res.Authentication | Should -BeNullOrEmpty + } + + It "set repository given Name and Authentication parameters" { + Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path + Set-PSResourceRepository -Name "testRepository" -Authentication @{VaultName = "test"; Secret = "test"} + $res = Get-PSResourceRepository -Name "testRepository" + $res.Name | Should -Be "testRepository" + $res.URL | Should -Contain $tmpDir1Path + $res.Priority | Should -Be 50 + $res.Trusted | Should -Be False + $res.Authentication["VaultName"] | Should -Be "test" + $res.Authentication["Secret"] | Should -Be "test" } It "not set repository and write error given just Name parameter" { @@ -102,12 +119,14 @@ Describe "Test Set-PSResourceRepository" { Unregister-PSResourceRepository -Name "PSGallery" Register-PSResourceRepository -Name "testRepository1" -URL $tmpDir1Path Register-PSResourceRepository -Name "testRepository2" -URL $tmpDir2Path + Register-PSResourceRepository -Name "testRepository3" -URL $tmpDir3Path Register-PSResourceRepository -PSGallery $hashtable1 = @{Name = "testRepository1"; URL = $tmpDir2Path}; $hashtable2 = @{Name = "testRepository2"; Priority = 25}; - $hashtable3 = @{Name = "PSGallery"; Trusted = $True}; - $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3 + $hashtable3 = @{Name = "testRepository3"; Authentication = @{VaultName = "test"; Secret = "test"}}; + $hashtable4 = @{Name = "PSGallery"; Trusted = $True}; + $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 Set-PSResourceRepository -Repositories $arrayOfHashtables $res = Get-PSResourceRepository -Name "testRepository1" @@ -115,18 +134,29 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir2Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False + $res.Authentication | Should -BeNullOrEmpty $res2 = Get-PSResourceRepository -Name "testRepository2" $res2.Name | Should -Be "testRepository2" $res2.URL.LocalPath | Should -Contain $tmpDir2Path $res2.Priority | Should -Be 25 $res2.Trusted | Should -Be False + $res2.Authentication | Should -BeNullOrEmpty - $res3 = Get-PSResourceRepository -Name $PSGalleryName - $res3.Name | Should -Be $PSGalleryName - $res3.URL | Should -Contain $PSGalleryURL + $res3 = Get-PSResourceRepository -Name "testRepository3" + $res3.Name | Should -Be "testRepository3" + $res3.URL | Should -Contain $tmpDir3Path $res3.Priority | Should -Be 50 - $res3.Trusted | Should -Be True + $res3.Trusted | Should -Be False + $res3.Authentication["VaultName"] | Should -Be "test" + $res3.Authentication["Secret"] | Should -Be "test" + + $res4 = Get-PSResourceRepository -Name $PSGalleryName + $res4.Name | Should -Be $PSGalleryName + $res4.URL | Should -Contain $PSGalleryURL + $res4.Priority | Should -Be 50 + $res4.Trusted | Should -Be True + $res4.Authentication | Should -BeNullOrEmpty } It "not set and throw error for trying to set PSGallery URL (NameParameterSet)" { @@ -135,6 +165,12 @@ Describe "Test Set-PSResourceRepository" { {Set-PSResourceRepository -Name "PSGallery" -URL $tmpDir1Path -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" } + It "not set and throw error for trying to set PSGallery Authentication (NameParameterSet)" { + Unregister-PSResourceRepository -Name "PSGallery" + Register-PSResourceRepository -PSGallery + {Set-PSResourceRepository -Name "PSGallery" -Authentication @{VaultName = "test"; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + } + It "not set repository and throw error for trying to set PSGallery URL (RepositoriesParameterSet)" { Unregister-PSResourceRepository -Name "PSGallery" Register-PSResourceRepository -PSGallery @@ -164,4 +200,63 @@ Describe "Test Set-PSResourceRepository" { $res.Trusted | Should -Be False $res.Priority | Should -Be 50 } -} + + It "not set repository and throw error for trying to set PSGallery Authentication (RepositoriesParameterSet)" { + Unregister-PSResourceRepository -Name "PSGallery" + Register-PSResourceRepository -PSGallery + + Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path + + $hashtable1 = @{Name = "PSGallery"; Authentication = @{VaultName = "test"; Secret = "test"}} + $hashtable2 = @{Name = "testRepository"; Priority = 25} + $arrayOfHashtables = $hashtable1, $hashtable2 + + Set-PSResourceRepository -Repositories $arrayOfHashtables -ErrorVariable err -ErrorAction SilentlyContinue + $err.Count | Should -Not -Be 0 + $err[0].FullyQualifiedErrorId | Should -BeExactly "ErrorSettingIndividualRepoFromRepositories,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + + $res = Get-PSResourceRepository -Name "testRepository" + $res.URL | Should -Contain $tmpDir1Path + $res.Priority | Should -Be 25 + $res.Trusted | Should -Be False + $res.Authentication | Should -BeNullOrEmpty + } + + It "not set and throw error if Authentication is invalid (NameParameterSet)" { + Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path + {Set-PSResourceRepository -Name "testRepository" -Authentication @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + {Set-PSResourceRepository -Name "testRepository" -Authentication @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + } + + It "not set and throw error if Authentication is invalid (RepositoriesParameterSet)" { + Register-PSResourceRepository -Name "testRepository1" -URL $tmpDir1Path + Register-PSResourceRepository -Name "testRepository2" -URL $tmpDir2Path + Register-PSResourceRepository -Name "testRepository3" -URL $tmpDir3Path + Register-PSResourceRepository -Name "testRepository4" -URL $tmpDir4Path + + $hashtable1 = @{Name = "testRepository1"; Priority = 25} + $hashtable2 = @{Name = "testRepository2"; Authentication = @{Secret = ""}} + $hashtable3 = @{Name = "testRepository3"; Authentication = @{VaultName = "test"; Secret = ""}} + $hashtable4 = @{Name = "testRepository4"; Authentication = @{}} + $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 + + Set-PSResourceRepository -Repositories $arrayOfHashtables -ErrorVariable err -ErrorAction SilentlyContinue + $err.Count | Should -Not -Be 0 + $err[0].FullyQualifiedErrorId | Should -BeExactly "ErrorSettingIndividualRepoFromRepositories,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + + $res = Get-PSResourceRepository -Name "testRepository1" + $res.URL | Should -Contain $tmpDir1Path + $res.Priority | Should -Be 25 + $res.Trusted | Should -Be False + $res.Authentication | Should -BeNullOrEmpty + + $res2 = Get-PSResourceRepository -Name "testRepository2" + $res2.Authentication | Should -BeNullOrEmpty + + $res3 = Get-PSResourceRepository -Name "testRepository3" + $res3.Authentication | Should -BeNullOrEmpty + + $res4 = Get-PSResourceRepository -Name "testRepository4" + $res4.Authentication | Should -BeNullOrEmpty + } +} \ No newline at end of file diff --git a/test/UnregisterPSResourceRepository.Tests.ps1 b/test/UnregisterPSResourceRepository.Tests.ps1 index 4359c4be9..9d889416b 100644 --- a/test/UnregisterPSResourceRepository.Tests.ps1 +++ b/test/UnregisterPSResourceRepository.Tests.ps1 @@ -3,7 +3,7 @@ Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force -Describe "Test Register-PSResourceRepository" { +Describe "Test Unregister-PSResourceRepository" { BeforeEach { Get-NewPSResourceRepositoryFile $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1" diff --git a/test/testRepositories.xml b/test/testRepositories.xml index b84e90f9d..d9d7fdf81 100644 --- a/test/testRepositories.xml +++ b/test/testRepositories.xml @@ -3,4 +3,4 @@ - + \ No newline at end of file diff --git a/test/testRepositoriesWithAuthentication.xml b/test/testRepositoriesWithAuthentication.xml new file mode 100644 index 000000000..8eed6de23 --- /dev/null +++ b/test/testRepositoriesWithAuthentication.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file From 63f3477ca9dd43cb996447b128d4a2a1b0baea34 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Mon, 30 Aug 2021 19:00:10 -0500 Subject: [PATCH 02/20] fix tests by comparing URL.LocalPaths --- test/RegisterPSResourceRepository.Tests.ps1 | 6 +++--- test/SetPSResourceRepository.Tests.ps1 | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index a2518ee8d..b58c1add4 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -54,7 +54,7 @@ Describe "Test Register-PSResourceRepository" { It "register repository given Name, URL, Trusted, Priority, Authentication (NameParameterSet)" { $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -Authentication @{VaultName = "testvault"; Secret = "testsecret"} -PassThru $res.Name | Should -Be "testRepository" - $res.URL | Should -Contain $tmpDir1Path + $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Trusted | Should -Be True $res.Priority | Should -Be 20 $res.Authentication["VaultName"] | Should -Be "testvault" @@ -112,7 +112,7 @@ Describe "Test Register-PSResourceRepository" { $res3.Priority | Should -Be 20 $res4 = Get-PSResourceRepository -Name "testRepository4" - $res4.URL | Should -Contain $tmpDir4Path + $res4.URL.LocalPath | Should -Contain $tmpDir4Path $res4.Trusted | Should -Be True $res4.Priority | Should -Be 30 $res4.Authentication["VaultName"] | Should -Be "testvault" @@ -161,7 +161,7 @@ Describe "Test Register-PSResourceRepository" { $res4.Priority | Should -Be 20 $res5 = Get-PSResourceRepository -Name "testRepository4" - $res5.URL | Should -Contain $tmpDir4Path + $res5.URL.LocalPath | Should -Contain $tmpDir4Path $res5.Trusted | Should -Be True $res5.Priority | Should -Be 30 $res5.Authentication["VaultName"] | Should -Be "testvault" diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 84e1a639e..2f2a3dd1a 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -65,7 +65,7 @@ Describe "Test Set-PSResourceRepository" { Set-PSResourceRepository -Name "testRepository" -Authentication @{VaultName = "test"; Secret = "test"} $res = Get-PSResourceRepository -Name "testRepository" $res.Name | Should -Be "testRepository" - $res.URL | Should -Contain $tmpDir1Path + $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False $res.Authentication["VaultName"] | Should -Be "test" @@ -145,7 +145,7 @@ Describe "Test Set-PSResourceRepository" { $res3 = Get-PSResourceRepository -Name "testRepository3" $res3.Name | Should -Be "testRepository3" - $res3.URL | Should -Contain $tmpDir3Path + $res3.URL.LocalPath | Should -Contain $tmpDir3Path $res3.Priority | Should -Be 50 $res3.Trusted | Should -Be False $res3.Authentication["VaultName"] | Should -Be "test" @@ -216,7 +216,7 @@ Describe "Test Set-PSResourceRepository" { $err[0].FullyQualifiedErrorId | Should -BeExactly "ErrorSettingIndividualRepoFromRepositories,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" $res = Get-PSResourceRepository -Name "testRepository" - $res.URL | Should -Contain $tmpDir1Path + $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 25 $res.Trusted | Should -Be False $res.Authentication | Should -BeNullOrEmpty @@ -245,7 +245,7 @@ Describe "Test Set-PSResourceRepository" { $err[0].FullyQualifiedErrorId | Should -BeExactly "ErrorSettingIndividualRepoFromRepositories,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" $res = Get-PSResourceRepository -Name "testRepository1" - $res.URL | Should -Contain $tmpDir1Path + $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 25 $res.Trusted | Should -Be False $res.Authentication | Should -BeNullOrEmpty From adf76173e7bf727d070b76f9a112706fee9c4ec6 Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Wed, 1 Sep 2021 12:15:57 -0700 Subject: [PATCH 03/20] Add new line --- test/testRepositories.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testRepositories.xml b/test/testRepositories.xml index d9d7fdf81..b84e90f9d 100644 --- a/test/testRepositories.xml +++ b/test/testRepositories.xml @@ -3,4 +3,4 @@ - \ No newline at end of file + From f4a267da88496b1527e5902ac3eded4a0407ebca Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Wed, 1 Sep 2021 16:28:56 -0700 Subject: [PATCH 04/20] Update test/SetPSResourceRepository.Tests.ps1 --- test/SetPSResourceRepository.Tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 2f2a3dd1a..d116fe25e 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -259,4 +259,4 @@ Describe "Test Set-PSResourceRepository" { $res4 = Get-PSResourceRepository -Name "testRepository4" $res4.Authentication | Should -BeNullOrEmpty } -} \ No newline at end of file +} From 1924defefa6b8b30212702f9088be284844d9fc4 Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Wed, 1 Sep 2021 16:29:22 -0700 Subject: [PATCH 05/20] Update .gitignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 465eb2190..87bc2da72 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ src/code/obj srcOld/code/bin srcOld/code/obj out -result.pester.xml \ No newline at end of file +result.pester.xml From f08ef0df783924e6ff30711defa469d53b52270e Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Wed, 1 Sep 2021 16:30:15 -0700 Subject: [PATCH 06/20] Update src/code/AuthenticationHelper.cs --- src/code/AuthenticationHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code/AuthenticationHelper.cs b/src/code/AuthenticationHelper.cs index aa8ec2e58..0ff1f783f 100644 --- a/src/code/AuthenticationHelper.cs +++ b/src/code/AuthenticationHelper.cs @@ -191,4 +191,4 @@ public static Collection InvokeScriptWithHost( } #endregion PowerShellInvoker -} \ No newline at end of file +} From d5474e47411c0bab59219b717487fcb9dbbf03ae Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Wed, 1 Sep 2021 16:30:27 -0700 Subject: [PATCH 07/20] Update src/code/RegisterPSResourceRepository.cs --- src/code/RegisterPSResourceRepository.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index 984085aae..a9c7803c6 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -90,7 +90,7 @@ class RegisterPSResourceRepository : PSCmdlet /// [Parameter(ParameterSetName = NameParameterSet)] [ValidateNotNullOrEmpty] - public Hashtable Authentication {get; set;} + public Hashtable Authentication { get; set; } /// /// Specifies a proxy server for the request, rather than a direct connection to the internet resource. From 15d8f63753897a7e5782377c06d6144c8564f916 Mon Sep 17 00:00:00 2001 From: alerickson <25858831+alerickson@users.noreply.github.com> Date: Wed, 1 Sep 2021 16:30:44 -0700 Subject: [PATCH 08/20] Update test/testRepositoriesWithAuthentication.xml --- test/testRepositoriesWithAuthentication.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testRepositoriesWithAuthentication.xml b/test/testRepositoriesWithAuthentication.xml index 8eed6de23..1be96724f 100644 --- a/test/testRepositoriesWithAuthentication.xml +++ b/test/testRepositoriesWithAuthentication.xml @@ -4,4 +4,4 @@ - \ No newline at end of file + From f598313a1cf5c55fefc1d40c72ca669fca032140 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Mon, 22 Nov 2021 12:40:51 -0600 Subject: [PATCH 09/20] rename Authentication parameter to CredentialInfo --- Docs/RegisterPSResourceRepository.md | 6 +- Docs/SetPSResourceRepository.md | 6 +- ...ationHelper.cs => CredentialInfoHelper.cs} | 10 ++-- src/code/FindHelper.cs | 18 +++--- src/code/InstallHelper.cs | 18 +++--- src/code/PSRepositoryInfo.cs | 8 +-- src/code/RegisterPSResourceRepository.cs | 40 ++++++------- src/code/RepositorySettings.cs | 56 +++++++++--------- src/code/SetPSResourceRepository.cs | 32 +++++----- test/GetPSResourceRepository.Tests.ps1 | 4 +- test/PSGetTestUtils.psm1 | 4 +- test/PublishPSResource.Tests.ps1 | 11 ++-- test/RegisterPSResourceRepository.Tests.ps1 | 46 +++++++-------- test/SetPSResourceRepository.Tests.ps1 | 58 +++++++++---------- ...=> testRepositoriesWithCredentialInfo.xml} | 0 15 files changed, 157 insertions(+), 160 deletions(-) rename src/code/{AuthenticationHelper.cs => CredentialInfoHelper.cs} (93%) rename test/{testRepositoriesWithAuthentication.xml => testRepositoriesWithCredentialInfo.xml} (100%) diff --git a/Docs/RegisterPSResourceRepository.md b/Docs/RegisterPSResourceRepository.md index 48d9d6376..1b5687211 100644 --- a/Docs/RegisterPSResourceRepository.md +++ b/Docs/RegisterPSResourceRepository.md @@ -10,7 +10,7 @@ The Register-PSResourceRepository cmdlet determines which repository will be the ### NameParameterSet (Default) ``` PowerShell -[[-Name] ] [-URL ] [-Priority ] [-Trusted] [-Authentication] [-WhatIf] [-Confirm] [] +[[-Name] ] [-URL ] [-Priority ] [-Trusted] [-CredentialInfo] [-WhatIf] [-Confirm] [] ``` ### PSGalleryParameterSet @@ -70,9 +70,9 @@ Type: SwitchParameter Parameter Sets: NameParameterSet, PSGalleryParameterSet ``` -### -Authentication +### -CredentialInfo -Specifies a hashtable of vault and secret names as Authentication information for the repository. +Specifies a hashtable of vault and secret names as CredentialInfo for the repository. ```yml Type: Hashtable diff --git a/Docs/SetPSResourceRepository.md b/Docs/SetPSResourceRepository.md index 7dbda4fd5..ea2406a61 100644 --- a/Docs/SetPSResourceRepository.md +++ b/Docs/SetPSResourceRepository.md @@ -13,7 +13,7 @@ The `-URL` for the PSGallery repository, which is pre-defined for this repositor ### NameParameterSet (Default) ``` PowerShell -[[-Name] ] [-URL ] [-Trusted] [-Priority ] [-Authentication ] [-WhatIf] [-Confirm] [] +[[-Name] ] [-URL ] [-Trusted] [-Priority ] [-CredentialInfo ] [-WhatIf] [-Confirm] [] ``` ### RepositoriesParameterSet @@ -70,9 +70,9 @@ Type: int Parameter Sets: NameParameterSet ``` -### -Authentication +### -CredentialInfo -Specifies a hashtable of vault and secret names as Authentication information for the repository. +Specifies a hashtable of vault and secret names as CredentialInfo for the repository. ```yml Type: Hashtable diff --git a/src/code/AuthenticationHelper.cs b/src/code/CredentialInfoHelper.cs similarity index 93% rename from src/code/AuthenticationHelper.cs rename to src/code/CredentialInfoHelper.cs index 0ff1f783f..ec3515b18 100644 --- a/src/code/AuthenticationHelper.cs +++ b/src/code/CredentialInfoHelper.cs @@ -10,9 +10,9 @@ namespace Microsoft.PowerShell.PowerShellGet.Cmdlets { /// - /// Authentication helper class includes functions to get repository credentials from Microsoft.PowerShell.SecretManagement if provided + /// CredentialInfo helper class includes functions to get repository credentials from Microsoft.PowerShell.SecretManagement if provided /// - internal class AuthenticationHelper + internal class CredentialInfoHelper { internal static readonly string VaultNameAttribute = "VaultName"; internal static readonly string SecretAttribute = "Secret"; @@ -21,12 +21,12 @@ internal class AuthenticationHelper private static readonly string SecretManagementModuleName = "Microsoft.PowerShell.SecretManagement"; - public AuthenticationHelper(PSCmdlet cmdletPassedIn) + public CredentialInfoHelper(PSCmdlet cmdletPassedIn) { _cmdletPassedIn = cmdletPassedIn; } - public string GetRepositoryAuthenticationPassword(string repositoryName, string vaultName, string secretName) + public string GetRepositoryCredentialInfoPassword(string repositoryName, string vaultName, string secretName) { var results = PowerShellInvoker.InvokeScriptWithHost( cmdlet: _cmdletPassedIn, @@ -53,7 +53,7 @@ public string GetRepositoryAuthenticationPassword(string repositoryName, string new PSInvalidOperationException( message: string.Format(CultureInfo.InvariantCulture, "Unable to read secret {0} from vault {1} for authenticating to PSResourceRepository {2}", secretName, vaultName, repositoryName), innerException: terminatingError), - "RepositoryAuthenticationCannotGetSecretFromVault", + "RepositoryCredentialInfoCannotGetSecretFromVault", ErrorCategory.InvalidOperation, this)); } diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index 4385a886e..6e72e68fa 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -183,7 +183,7 @@ public IEnumerable FindByResourceName( foreach (var pkg in SearchFromRepository( repositoryName: repositoriesToSearch[i].Name, repositoryUrl: repositoriesToSearch[i].Url, - repositoryAuthentication: repositoriesToSearch[i].Authentication)) + repositoryCredentialInfo: repositoriesToSearch[i].CredentialInfo)) { yield return pkg; } @@ -197,7 +197,7 @@ public IEnumerable FindByResourceName( private IEnumerable SearchFromRepository( string repositoryName, Uri repositoryUrl, - Hashtable repositoryAuthentication) + Hashtable repositoryCredentialInfo) { PackageSearchResource resourceSearch; PackageMetadataResource resourceMetadata; @@ -234,22 +234,22 @@ private IEnumerable SearchFromRepository( // HTTP, HTTPS, FTP Uri schemes (only other Uri schemes allowed by RepositorySettings.Read() API) PackageSource source = new PackageSource(repositoryUrl.ToString()); - // Explicitly passed in Credential takes precedence over repository Authentication + // Explicitly passed in Credential takes precedence over repository CredentialInfo if (_credential != null) { string password = new NetworkCredential(string.Empty, _credential.Password).Password; source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), _credential.UserName, password, true, null); _cmdletPassedIn.WriteVerbose("credential successfully set for repository: " + repositoryName); } - else if (repositoryAuthentication != null) + else if (repositoryCredentialInfo != null) { - var authHelper = new AuthenticationHelper(_cmdletPassedIn); - string password = authHelper.GetRepositoryAuthenticationPassword( + var authHelper = new CredentialInfoHelper(_cmdletPassedIn); + string password = authHelper.GetRepositoryCredentialInfoPassword( repositoryName, - repositoryAuthentication[AuthenticationHelper.VaultNameAttribute].ToString(), - repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString()); + repositoryCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString(), + repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString()); - source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString(), password, true, null); + source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString(), password, true, null); _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repositoryName); } diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index c0fbbfbbf..f9dbc5e01 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -220,7 +220,7 @@ private void ProcessRepositories( pkgsFromRepoToInstall, repoName, repo.Url.AbsoluteUri, - repo.Authentication, + repo.CredentialInfo, credential, isLocalRepo); @@ -283,7 +283,7 @@ private List InstallPackage( IEnumerable pkgsToInstall, string repoName, string repoUrl, - Hashtable repositoryAuthentication, + Hashtable repositoryCredentialInfo, PSCredential credential, bool isLocalRepo) { @@ -368,21 +368,21 @@ private List InstallPackage( // Set up NuGet API resource for download PackageSource source = new PackageSource(repoUrl); - // Explicitly passed in Credential takes precedence over repository Authentication + // Explicitly passed in Credential takes precedence over repository CredentialInfo if (credential != null) { string password = new NetworkCredential(string.Empty, credential.Password).Password; source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, credential.UserName, password, true, null); } - else if (repositoryAuthentication != null) + else if (repositoryCredentialInfo != null) { - var authHelper = new AuthenticationHelper(_cmdletPassedIn); - string password = authHelper.GetRepositoryAuthenticationPassword( + var authHelper = new CredentialInfoHelper(_cmdletPassedIn); + string password = authHelper.GetRepositoryCredentialInfoPassword( repoName, - repositoryAuthentication[AuthenticationHelper.VaultNameAttribute].ToString(), - repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString()); + repositoryCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString(), + repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString()); - source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repositoryAuthentication[AuthenticationHelper.SecretAttribute].ToString(), password, true, null); + source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString(), password, true, null); } var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider); SourceRepository repository = new SourceRepository(source, provider); diff --git a/src/code/PSRepositoryInfo.cs b/src/code/PSRepositoryInfo.cs index 0ab0c2660..c84819dc0 100644 --- a/src/code/PSRepositoryInfo.cs +++ b/src/code/PSRepositoryInfo.cs @@ -14,13 +14,13 @@ public sealed class PSRepositoryInfo { #region Constructor - public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, Hashtable authentication) + public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, Hashtable credentialInfo) { Name = name; Url = url; Priority = priority; Trusted = trusted; - Authentication = authentication; + CredentialInfo = credentialInfo; } #endregion @@ -48,9 +48,9 @@ public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, Hashta public int Priority { get; } /// - /// the Authentication for the repository + /// the CredentialInfo for the repository /// - public Hashtable Authentication { get; } + public Hashtable CredentialInfo { get; } #endregion } diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index ea7611958..553dd0da6 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -85,11 +85,11 @@ class RegisterPSResourceRepository : PSCmdlet public int Priority { get; set; } = defaultPriority; /// - /// Specifies a hashtable of vault and secret names as Authentication information for the repository. + /// Specifies a hashtable of vault and secret names as CredentialInfo for the repository. /// [Parameter(ParameterSetName = NameParameterSet)] [ValidateNotNullOrEmpty] - public Hashtable Authentication { get; set; } + public Hashtable CredentialInfo { get; set; } /// /// Specifies a proxy server for the request, rather than a direct connection to the internet resource. @@ -144,7 +144,7 @@ protected override void ProcessRecord() try { - items.Add(NameParameterSetHelper(Name, _url, Priority, Trusted, Authentication)); + items.Add(NameParameterSetHelper(Name, _url, Priority, Trusted, CredentialInfo)); } catch (Exception e) { @@ -201,7 +201,7 @@ protected override void ProcessRecord() } } - private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) + private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) { // remove trailing and leading whitespaces, and if Name is just whitespace Name should become null now and be caught by following condition repoName = repoName.Trim(' '); @@ -215,12 +215,12 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl throw new ArgumentException("Invalid url, must be one of the following Uri schemes: HTTPS, HTTP, FTP, File Based"); } - if (repoAuthentication != null) + if (repoCredentialInfo != null) { - if (!repoAuthentication.ContainsKey(AuthenticationHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.VaultNameAttribute].ToString()) - || !repoAuthentication.ContainsKey(AuthenticationHelper.SecretAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.SecretAttribute].ToString())) + if (!repoCredentialInfo.ContainsKey(CredentialInfoHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString()) + || !repoCredentialInfo.ContainsKey(CredentialInfoHelper.SecretAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString())) { - throw new ArgumentException($"Invalid Authentication, must include {AuthenticationHelper.VaultNameAttribute} and {AuthenticationHelper.SecretAttribute} key/(non-empty) value pairs"); + throw new ArgumentException($"Invalid CredentialInfo, must include {CredentialInfoHelper.VaultNameAttribute} and {CredentialInfoHelper.SecretAttribute} key/(non-empty) value pairs"); } } @@ -230,10 +230,10 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl return null; } - return RepositorySettings.Add(repoName, repoUrl, repoPriority, repoTrusted, repoAuthentication); + return RepositorySettings.Add(repoName, repoUrl, repoPriority, repoTrusted, repoCredentialInfo); } - private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) + private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) { if (repoName.Equals("PSGallery", StringComparison.OrdinalIgnoreCase)) { @@ -241,7 +241,7 @@ private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, in throw new ArgumentException("Cannot register PSGallery with -Name parameter. Try: Register-PSResourceRepository -PSGallery"); } - return AddToRepositoryStoreHelper(repoName, repoUrl, repoPriority, repoTrusted, repoAuthentication); + return AddToRepositoryStoreHelper(repoName, repoUrl, repoPriority, repoTrusted, repoCredentialInfo); } private PSRepositoryInfo PSGalleryParameterSetHelper(int repoPriority, bool repoTrusted) @@ -258,10 +258,10 @@ private List RepositoriesParameterSetHelper() { if (repo.ContainsKey(PSGalleryRepoName)) { - if (repo.ContainsKey("Name") || repo.ContainsKey("Url") || repo.ContainsKey("Authentication")) + if (repo.ContainsKey("Name") || repo.ContainsKey("Url") || repo.ContainsKey("CredentialInfo")) { WriteError(new ErrorRecord( - new PSInvalidOperationException("Repository hashtable cannot contain PSGallery key with -Name, -URL and/or -Authentication key value pairs"), + new PSInvalidOperationException("Repository hashtable cannot contain PSGallery key with -Name, -URL and/or -CredentialInfo key value pairs"), "NotProvideNameUrlAuthForPSGalleryRepositoriesParameterSetRegistration", ErrorCategory.InvalidArgument, this)); @@ -338,15 +338,15 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) return null; } - Hashtable repoAuthentication = repo["Authentication"] as Hashtable; - if (repoAuthentication != null) + Hashtable repoCredentialInfo = repo["CredentialInfo"] as Hashtable; + if (repoCredentialInfo != null) { - if (!repoAuthentication.ContainsKey(AuthenticationHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.VaultNameAttribute].ToString()) - || !repoAuthentication.ContainsKey(AuthenticationHelper.SecretAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.SecretAttribute].ToString())) + if (!repoCredentialInfo.ContainsKey(CredentialInfoHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString()) + || !repoCredentialInfo.ContainsKey(CredentialInfoHelper.SecretAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString())) { WriteError(new ErrorRecord( - new PSInvalidOperationException($"Invalid Authentication, must include {AuthenticationHelper.VaultNameAttribute} and {AuthenticationHelper.SecretAttribute} key/(non-empty) value pairs"), - "InvalidAuthentication", + new PSInvalidOperationException($"Invalid CredentialInfo, must include {CredentialInfoHelper.VaultNameAttribute} and {CredentialInfoHelper.SecretAttribute} key/(non-empty) value pairs"), + "InvalidCredentialInfo", ErrorCategory.InvalidArgument, this)); return null; @@ -360,7 +360,7 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) repoURL, repo.ContainsKey("Priority") ? Convert.ToInt32(repo["Priority"].ToString()) : defaultPriority, repo.ContainsKey("Trusted") ? Convert.ToBoolean(repo["Trusted"].ToString()) : defaultTrusted, - repoAuthentication); + repoCredentialInfo); } catch (Exception e) { diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs index 0377fe9ea..8e1eb1d1e 100644 --- a/src/code/RepositorySettings.cs +++ b/src/code/RepositorySettings.cs @@ -83,7 +83,7 @@ public static void CheckRepositoryStore() /// Returns: PSRepositoryInfo containing information about the repository just added to the repository store /// /// - public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) + public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) { try { @@ -107,9 +107,9 @@ public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriorit new XAttribute("Trusted", repoTrusted) ); - if(repoAuthentication != null) { - newElement.Add(new XAttribute(VaultNameAttribute, repoAuthentication[VaultNameAttribute])); - newElement.Add(new XAttribute(SecretAttribute, repoAuthentication[SecretAttribute])); + if(repoCredentialInfo != null) { + newElement.Add(new XAttribute(VaultNameAttribute, repoCredentialInfo[VaultNameAttribute])); + newElement.Add(new XAttribute(SecretAttribute, repoCredentialInfo[SecretAttribute])); } root.Add(newElement); @@ -122,14 +122,14 @@ public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriorit throw new PSInvalidOperationException(String.Format("Adding to repository store failed: {0}", e.Message)); } - return new PSRepositoryInfo(repoName, repoURL, repoPriority, repoTrusted, repoAuthentication); + return new PSRepositoryInfo(repoName, repoURL, repoPriority, repoTrusted, repoCredentialInfo); } /// /// Updates a repository name, URL, priority, or installation policy /// Returns: void /// - public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPriority, bool? repoTrusted, Hashtable repoAuthentication) + public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPriority, bool? repoTrusted, Hashtable repoCredentialInfo) { PSRepositoryInfo updatedRepo; try @@ -167,22 +167,22 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio node.Attribute("Trusted").Value = repoTrusted.ToString(); } - // A null Authentication value passed in signifies that Authentication information was not attempted to be set. - // So only set VaultName and Secret attributes if non-null value passed in for repoAuthentication - if (repoAuthentication != null) + // A null CredentialInfo value passed in signifies that CredentialInfo was not attempted to be set. + // So only set VaultName and Secret attributes if non-null value passed in for repoCredentialInfo + if (repoCredentialInfo != null) { if (node.Attribute(VaultNameAttribute) == null) { - node.Add(new XAttribute(VaultNameAttribute, repoAuthentication[VaultNameAttribute])); + node.Add(new XAttribute(VaultNameAttribute, repoCredentialInfo[VaultNameAttribute])); } else { - node.Attribute(VaultNameAttribute).Value = repoAuthentication[VaultNameAttribute].ToString(); + node.Attribute(VaultNameAttribute).Value = repoCredentialInfo[VaultNameAttribute].ToString(); } if (node.Attribute(SecretAttribute) == null) { - node.Add(new XAttribute(SecretAttribute, repoAuthentication[SecretAttribute])); + node.Add(new XAttribute(SecretAttribute, repoCredentialInfo[SecretAttribute])); } else { - node.Attribute(SecretAttribute).Value = repoAuthentication[SecretAttribute].ToString(); + node.Attribute(SecretAttribute).Value = repoCredentialInfo[SecretAttribute].ToString(); } } @@ -192,8 +192,8 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio throw new PSInvalidOperationException(String.Format("Unable to read incorrectly formatted URL for repo {0}", repoName)); } - // Create Authentication based on new values or whether it was empty to begin with - Hashtable thisAuthentication = !string.IsNullOrEmpty(node.Attribute(VaultNameAttribute)?.Value) && !string.IsNullOrEmpty(node.Attribute(SecretAttribute)?.Value) + // Create CredentialInfo based on new values or whether it was empty to begin with + Hashtable thisCredentialInfo = !string.IsNullOrEmpty(node.Attribute(VaultNameAttribute)?.Value) && !string.IsNullOrEmpty(node.Attribute(SecretAttribute)?.Value) ? new Hashtable() { { VaultNameAttribute, node.Attribute(VaultNameAttribute).Value }, { SecretAttribute, node.Attribute(SecretAttribute).Value } @@ -204,7 +204,7 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio thisUrl, Int32.Parse(node.Attribute("Priority").Value), Boolean.Parse(node.Attribute("Trusted").Value), - thisAuthentication); + thisCredentialInfo); // Close the file root.Save(FullRepositoryPath); @@ -285,14 +285,14 @@ public static List Read(string[] repoNames, out string[] error continue; } - Hashtable thisAuthentication = null; - string authErrorMessage = $"Repository {repo.Attribute("Name")} has invalid Authentication information. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; + Hashtable thisCredentialInfo = null; + string authErrorMessage = $"Repository {repo.Attribute("Name")} has invalid CredentialInfo. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; // both keys present if (repo.Attribute(VaultNameAttribute) != null && repo.Attribute(SecretAttribute) != null) { // both values non-empty - // = valid authentication + // = valid credentialInfo if (!string.IsNullOrEmpty(repo.Attribute(VaultNameAttribute).Value) && !string.IsNullOrEmpty(repo.Attribute(SecretAttribute).Value)) { - thisAuthentication = new Hashtable() { + thisCredentialInfo = new Hashtable() { { VaultNameAttribute, repo.Attribute(VaultNameAttribute).Value }, { SecretAttribute, repo.Attribute(SecretAttribute).Value } }; @@ -304,7 +304,7 @@ public static List Read(string[] repoNames, out string[] error } // both keys are missing else if (repo.Attribute(VaultNameAttribute) == null && repo.Attribute(SecretAttribute) == null) { - // = valid authentication, do nothing + // = valid credentialInfo, do nothing } // one of the keys is missing else { @@ -316,7 +316,7 @@ public static List Read(string[] repoNames, out string[] error thisUrl, Int32.Parse(repo.Attribute("Priority").Value), Boolean.Parse(repo.Attribute("Trusted").Value), - thisAuthentication); + thisCredentialInfo); foundRepos.Add(currentRepoItem); } @@ -338,14 +338,14 @@ public static List Read(string[] repoNames, out string[] error continue; } - Hashtable thisAuthentication = null; - string authErrorMessage = $"Repository {node.Attribute("Name")} has invalid Authentication information. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; + Hashtable thisCredentialInfo = null; + string authErrorMessage = $"Repository {node.Attribute("Name")} has invalid CredentialInfo. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; // both keys present if (node.Attribute(VaultNameAttribute) != null && node.Attribute(SecretAttribute) != null) { // both values non-empty - // = valid authentication + // = valid credentialInfo if (!string.IsNullOrEmpty(node.Attribute(VaultNameAttribute).Value) && !string.IsNullOrEmpty(node.Attribute(SecretAttribute).Value)) { - thisAuthentication = new Hashtable() { + thisCredentialInfo = new Hashtable() { { VaultNameAttribute, node.Attribute(VaultNameAttribute).Value }, { SecretAttribute, node.Attribute(SecretAttribute).Value } }; @@ -357,7 +357,7 @@ public static List Read(string[] repoNames, out string[] error } // both keys are missing else if (node.Attribute(VaultNameAttribute) == null && node.Attribute(SecretAttribute) == null) { - // = valid authentication, do nothing + // = valid credentialInfo, do nothing } // one of the keys is missing else { @@ -369,7 +369,7 @@ public static List Read(string[] repoNames, out string[] error thisUrl, Int32.Parse(node.Attribute("Priority").Value), Boolean.Parse(node.Attribute("Trusted").Value), - thisAuthentication); + thisCredentialInfo); foundRepos.Add(currentRepoItem); } diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs index 74f4451bf..baf997e27 100644 --- a/src/code/SetPSResourceRepository.cs +++ b/src/code/SetPSResourceRepository.cs @@ -83,11 +83,11 @@ public SwitchParameter Trusted public int Priority { get; set; } = DefaultPriority; /// - /// Specifies a hashtable of vault and secret names as Authentication information for the repository. + /// Specifies a hashtable of vault and secret names as CredentialInfo for the repository. /// [Parameter(ParameterSetName = NameParameterSet)] [ValidateNotNullOrEmpty] - public Hashtable Authentication {get; set;} + public Hashtable CredentialInfo {get; set;} /// /// When specified, displays the successfully registered repository and its information @@ -122,7 +122,7 @@ protected override void ProcessRecord() case NameParameterSet: try { - items.Add(UpdateRepositoryStoreHelper(Name, _url, Priority, Trusted, Authentication)); + items.Add(UpdateRepositoryStoreHelper(Name, _url, Priority, Trusted, CredentialInfo)); } catch (Exception e) { @@ -163,7 +163,7 @@ protected override void ProcessRecord() } } - private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoAuthentication) + private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) { if (repoUrl != null && !(repoUrl.Scheme == Uri.UriSchemeHttp || repoUrl.Scheme == Uri.UriSchemeHttps || repoUrl.Scheme == Uri.UriSchemeFtp || repoUrl.Scheme == Uri.UriSchemeFile)) { @@ -184,29 +184,29 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr throw new ArgumentException("The PSGallery repository has a pre-defined URL. Setting the -URL parameter for this repository is not allowed, instead try running 'Register-PSResourceRepository -PSGallery'."); } - // check PSGallery Authentication is not trying to be set - if (repoName.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) && repoAuthentication != null) + // check PSGallery CredentialInfo is not trying to be set + if (repoName.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) && repoCredentialInfo != null) { - throw new ArgumentException("The PSGallery repository does not require authentication. Setting the -Authentication parameter for this repository is not allowed, instead try running 'Register-PSResourceRepository -PSGallery'."); + throw new ArgumentException("The PSGallery repository does not require authentication. Setting the -CredentialInfo parameter for this repository is not allowed, instead try running 'Register-PSResourceRepository -PSGallery'."); } // determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable) bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?(); - if (repoAuthentication != null) + if (repoCredentialInfo != null) { - if (!repoAuthentication.ContainsKey(AuthenticationHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.VaultNameAttribute].ToString()) - || !repoAuthentication.ContainsKey(AuthenticationHelper.SecretAttribute) || string.IsNullOrEmpty(repoAuthentication[AuthenticationHelper.SecretAttribute].ToString())) + if (!repoCredentialInfo.ContainsKey(CredentialInfoHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString()) + || !repoCredentialInfo.ContainsKey(CredentialInfoHelper.SecretAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString())) { - throw new ArgumentException($"Invalid Authentication, must include {AuthenticationHelper.VaultNameAttribute} and {AuthenticationHelper.SecretAttribute} key/(non-empty) value pairs"); + throw new ArgumentException($"Invalid CredentialInfo, must include {CredentialInfoHelper.VaultNameAttribute} and {CredentialInfoHelper.SecretAttribute} key/(non-empty) value pairs"); } } - // determine if either 1 of 4 values are attempting to be set: URL, Priority, Trusted, Authentication. + // determine if either 1 of 4 values are attempting to be set: URL, Priority, Trusted, CredentialInfo. // if none are (i.e only Name parameter was provided, write error) - if(repoUrl == null && repoPriority == DefaultPriority && _trustedNullable == null && repoAuthentication == null) + if(repoUrl == null && repoPriority == DefaultPriority && _trustedNullable == null && repoCredentialInfo == null) { - throw new ArgumentException("Either URL, Priority, Trusted or Authentication parameters must be requested to be set"); + throw new ArgumentException("Either URL, Priority, Trusted or CredentialInfo parameters must be requested to be set"); } WriteVerbose("All required values to set repository provided, calling internal Update() API now"); @@ -214,7 +214,7 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr { return null; } - return RepositorySettings.Update(repoName, repoUrl, repoPriority, _trustedNullable, repoAuthentication); + return RepositorySettings.Update(repoName, repoUrl, repoPriority, _trustedNullable, repoCredentialInfo); } private List RepositoriesParameterSetHelper() @@ -282,7 +282,7 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) repoURL, repo.ContainsKey("Priority") ? Convert.ToInt32(repo["Priority"].ToString()) : DefaultPriority, repoTrusted, - repo["Authentication"] as Hashtable); + repo["CredentialInfo"] as Hashtable); } catch (Exception e) { diff --git a/test/GetPSResourceRepository.Tests.ps1 b/test/GetPSResourceRepository.Tests.ps1 index 486c9cd87..37b642cc1 100644 --- a/test/GetPSResourceRepository.Tests.ps1 +++ b/test/GetPSResourceRepository.Tests.ps1 @@ -83,8 +83,8 @@ Describe "Test Get-PSResourceRepository" { } } - It "given invalid and valid Authentication information, get valid ones and write error for non valid ones" { - Get-NewPSResourceRepositoryFileWithAuthentication + It "given invalid and valid CredentialInfo, get valid ones and write error for non valid ones" { + Get-NewPSResourceRepositoryFileWithCredentialInfo $res = Get-PSResourceRepository -Name "localtestrepo*" -ErrorVariable err -ErrorAction SilentlyContinue $err.Count | Should -Not -Be 0 diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1 index 97ed154d2..b0620b4d9 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -214,7 +214,7 @@ function Get-RevertPSResourceRepositoryFile { } } -function Get-NewPSResourceRepositoryFileWithAuthentication { +function Get-NewPSResourceRepositoryFileWithCredentialInfo { # register our own repositories with desired priority $powerShellGetPath = Join-Path -Path ([Environment]::GetFolderPath([System.Environment+SpecialFolder]::LocalApplicationData)) -ChildPath "PowerShellGet" $originalXmlFilePath = Join-Path -Path $powerShellGetPath -ChildPath "PSResourceRepository.xml" @@ -229,7 +229,7 @@ function Get-NewPSResourceRepositoryFileWithAuthentication { $null = New-Item -Path $powerShellGetPath -ItemType Directory -Verbose } - $fileToCopy = Join-Path -Path $PSScriptRoot -ChildPath "testRepositoriesWithAuthentication.xml" + $fileToCopy = Join-Path -Path $PSScriptRoot -ChildPath "testRepositoriesWithCredentialInfo.xml" Copy-Item -Path $fileToCopy -Destination $originalXmlFilePath -Force -Verbose } diff --git a/test/PublishPSResource.Tests.ps1 b/test/PublishPSResource.Tests.ps1 index 2bc5fc7ec..d906d5ee7 100644 --- a/test/PublishPSResource.Tests.ps1 +++ b/test/PublishPSResource.Tests.ps1 @@ -38,7 +38,7 @@ Describe "Test Publish-PSResource" { } # Create temp destination path - $script:destinationPath = Join-Path -Path $TestDrive -ChildPath "tmpDestinationPath" + $script:destinationPath = [IO.Path]::GetFullPath((Join-Path -Path $TestDrive -ChildPath "tmpDestinationPath")) New-Item $script:destinationPath -ItemType directory -Force } AfterAll { @@ -266,16 +266,13 @@ Describe "Test Publish-PSResource" { $version = "1.0.0" New-ModuleManifest -Path (Join-Path -Path $script:PublishModuleBase -ChildPath "$script:PublishModuleName.psd1") -ModuleVersion $version -Description "$script:PublishModuleName module" - $tmpPath = Join-Path -Path $TestDrive -ChildPath "testtmppath" - New-Item $tmpPath -Itemtype directory -Force - - Publish-PSResource -Path $script:PublishModuleBase -Repository $testRepository2 -DestinationPath $tmpPath + Publish-PSResource -Path $script:PublishModuleBase -Repository $testRepository2 -DestinationPath $script:destinationPath $expectedPath = Join-Path -Path $script:repositoryPath2 -ChildPath "$script:PublishModuleName.$version.nupkg" (Get-ChildItem $script:repositoryPath2).FullName | Should -Be $expectedPath - $expectedPath = Join-Path -Path $tmpPath -ChildPath "$script:PublishModuleName.$version.nupkg" - (Get-ChildItem $tmpPath).FullName | Should -Be $expectedPath + $expectedPath = Join-Path -Path $script:destinationPath -ChildPath "$script:PublishModuleName.$version.nupkg" + (Get-ChildItem $script:destinationPath).FullName | Should -Be $expectedPath } } diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index c3d6cfe49..23dd08d89 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -51,14 +51,14 @@ Describe "Test Register-PSResourceRepository" { $res.Priority | Should -Be 20 } - It "register repository given Name, URL, Trusted, Priority, Authentication (NameParameterSet)" { - $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -Authentication @{VaultName = "testvault"; Secret = "testsecret"} -PassThru + It "register repository given Name, URL, Trusted, Priority, CredentialInfo (NameParameterSet)" { + $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo @{VaultName = "testvault"; Secret = "testsecret"} -PassThru $res.Name | Should -Be "testRepository" $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Trusted | Should -Be True $res.Priority | Should -Be 20 - $res.Authentication["VaultName"] | Should -Be "testvault" - $res.Authentication["Secret"] | Should -Be "testsecret" + $res.CredentialInfo["VaultName"] | Should -Be "testvault" + $res.CredentialInfo["Secret"] | Should -Be "testsecret" } It "register repository with PSGallery parameter (PSGalleryParameterSet)" { @@ -92,7 +92,7 @@ Describe "Test Register-PSResourceRepository" { $hashtable1 = @{Name = "testRepository"; URL = $tmpDir1Path} $hashtable2 = @{Name = "testRepository2"; URL = $tmpDir2Path; Trusted = $True} $hashtable3 = @{Name = "testRepository3"; URL = $tmpDir3Path; Trusted = $True; Priority = 20} - $hashtable4 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; Authentication = @{VaultName = "testvault"; Secret = "testsecret"}} + $hashtable4 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = @{VaultName = "testvault"; Secret = "testsecret"}} $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 Register-PSResourceRepository -Repositories $arrayOfHashtables @@ -115,8 +115,8 @@ Describe "Test Register-PSResourceRepository" { $res4.URL.LocalPath | Should -Contain $tmpDir4Path $res4.Trusted | Should -Be True $res4.Priority | Should -Be 30 - $res4.Authentication["VaultName"] | Should -Be "testvault" - $res4.Authentication["Secret"] | Should -Be "testsecret" + $res4.CredentialInfo["VaultName"] | Should -Be "testvault" + $res4.CredentialInfo["Secret"] | Should -Be "testsecret" } It "register repositories with Repositories parameter, psgallery style repository (RepositoriesParameterSet)" { @@ -135,7 +135,7 @@ Describe "Test Register-PSResourceRepository" { $hashtable2 = @{Name = "testRepository"; URL = $tmpDir1Path} $hashtable3 = @{Name = "testRepository2"; URL = $tmpDir2Path; Trusted = $True} $hashtable4 = @{Name = "testRepository3"; URL = $tmpDir3Path; Trusted = $True; Priority = 20} - $hashtable5 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; Authentication = @{VaultName = "testvault"; Secret = "testsecret"}} + $hashtable5 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = @{VaultName = "testvault"; Secret = "testsecret"}} $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4, $hashtable5 Register-PSResourceRepository -Repositories $arrayOfHashtables @@ -164,8 +164,8 @@ Describe "Test Register-PSResourceRepository" { $res5.URL.LocalPath | Should -Contain $tmpDir4Path $res5.Trusted | Should -Be True $res5.Priority | Should -Be 30 - $res5.Authentication["VaultName"] | Should -Be "testvault" - $res5.Authentication["Secret"] | Should -Be "testsecret" + $res5.CredentialInfo["VaultName"] | Should -Be "testvault" + $res5.CredentialInfo["Secret"] | Should -Be "testsecret" } It "not register repository when Name is provided but URL is not" { @@ -184,14 +184,14 @@ Describe "Test Register-PSResourceRepository" { {Register-PSResourceRepository -Name " " -URL $tmpDir1Path -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } - It "not register if Authentication is missing VaultName or Secret" { - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + It "not register if CredentialInfo is missing VaultName or Secret" { + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } - It "not register if Authentication values are empty" { - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Authentication @{VaultName = ""; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + It "not register if CredentialInfo values are empty" { + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{VaultName = ""; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } It "not register PSGallery with NameParameterSet" { @@ -199,15 +199,15 @@ Describe "Test Register-PSResourceRepository" { } # this error message comes from the parameter cmdlet tags (earliest point of detection) - It "not register PSGallery when PSGallery parameter provided with Name, URL or Authentication" { + It "not register PSGallery when PSGallery parameter provided with Name, URL or CredentialInfo" { {Register-PSResourceRepository -PSGallery -Name $PSGalleryName -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" {Register-PSResourceRepository -PSGallery -URL $PSGalleryURL -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - {Register-PSResourceRepository -PSGallery -Authentication @{VaultName = "testvault"; Secret = "testsecret"} -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -PSGallery -CredentialInfo @{VaultName = "testvault"; Secret = "testsecret"} -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } $testCases = @{Type = "Name key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; Name=$PSGalleryName}}, @{Type = "URL key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; URL=$PSGalleryURL}}, - @{Type = "Authentication key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; Authentication = @{VaultName = "test"; Secret = "test"}}} + @{Type = "CredentialInfo key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; CredentialInfo = @{VaultName = "test"; Secret = "test"}}} It "not register incorrectly formatted PSGallery type repo among correct ones when incorrect type is " -TestCases $testCases { param($Type, $IncorrectHashTable) @@ -236,10 +236,10 @@ Describe "Test Register-PSResourceRepository" { @{Type = "-Name is PSGallery"; IncorrectHashTable = @{Name = "PSGallery"; URL = $tmpDir1Path}; ErrorId = "PSGalleryProvidedAsNameRepoPSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, @{Type = "-URL not specified"; IncorrectHashTable = @{Name = "testRepository"}; ErrorId = "NullURLForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, @{Type = "-URL is not valid scheme"; IncorrectHashTable = @{Name = "testRepository"; URL="www.google.com"}; ErrorId = "InvalidUri,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-Authentication is missing VaultName"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{Secret = "test"}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-Authentication is missing Secret"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{VaultName = "test"}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-Authentication-VaultName value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{VaultName = ""; Secret = "test"}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-Authentication-Secret value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; Authentication = @{VaultName = "test"; Secret = ""}}; ErrorId = "InvalidAuthentication,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} + @{Type = "-CredentialInfo is missing VaultName"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{Secret = "test"}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-CredentialInfo is missing Secret"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{VaultName = "test"}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-CredentialInfo-VaultName value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{VaultName = ""; Secret = "test"}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-CredentialInfo-Secret value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{VaultName = "test"; Secret = ""}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} It "not register incorrectly formatted Name type repo among correct ones when incorrect type is " -TestCases $testCases2 { param($Type, $IncorrectHashTable, $ErrorId) diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index d116fe25e..74d223bdb 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -35,7 +35,7 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir2Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False - $res.Authentication | Should -BeNullOrEmpty + $res.CredentialInfo | Should -BeNullOrEmpty } It "set repository given Name and Priority parameters" { @@ -46,7 +46,7 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 25 $res.Trusted | Should -Be False - $res.Authentication | Should -BeNullOrEmpty + $res.CredentialInfo | Should -BeNullOrEmpty } It "set repository given Name and Trusted parameters" { @@ -57,19 +57,19 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be True - $res.Authentication | Should -BeNullOrEmpty + $res.CredentialInfo | Should -BeNullOrEmpty } - It "set repository given Name and Authentication parameters" { + It "set repository given Name and CredentialInfo parameters" { Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path - Set-PSResourceRepository -Name "testRepository" -Authentication @{VaultName = "test"; Secret = "test"} + Set-PSResourceRepository -Name "testRepository" -CredentialInfo @{VaultName = "test"; Secret = "test"} $res = Get-PSResourceRepository -Name "testRepository" $res.Name | Should -Be "testRepository" $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False - $res.Authentication["VaultName"] | Should -Be "test" - $res.Authentication["Secret"] | Should -Be "test" + $res.CredentialInfo["VaultName"] | Should -Be "test" + $res.CredentialInfo["Secret"] | Should -Be "test" } It "not set repository and write error given just Name parameter" { @@ -124,7 +124,7 @@ Describe "Test Set-PSResourceRepository" { $hashtable1 = @{Name = "testRepository1"; URL = $tmpDir2Path}; $hashtable2 = @{Name = "testRepository2"; Priority = 25}; - $hashtable3 = @{Name = "testRepository3"; Authentication = @{VaultName = "test"; Secret = "test"}}; + $hashtable3 = @{Name = "testRepository3"; CredentialInfo = @{VaultName = "test"; Secret = "test"}}; $hashtable4 = @{Name = "PSGallery"; Trusted = $True}; $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 @@ -134,29 +134,29 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir2Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False - $res.Authentication | Should -BeNullOrEmpty + $res.CredentialInfo | Should -BeNullOrEmpty $res2 = Get-PSResourceRepository -Name "testRepository2" $res2.Name | Should -Be "testRepository2" $res2.URL.LocalPath | Should -Contain $tmpDir2Path $res2.Priority | Should -Be 25 $res2.Trusted | Should -Be False - $res2.Authentication | Should -BeNullOrEmpty + $res2.CredentialInfo | Should -BeNullOrEmpty $res3 = Get-PSResourceRepository -Name "testRepository3" $res3.Name | Should -Be "testRepository3" $res3.URL.LocalPath | Should -Contain $tmpDir3Path $res3.Priority | Should -Be 50 $res3.Trusted | Should -Be False - $res3.Authentication["VaultName"] | Should -Be "test" - $res3.Authentication["Secret"] | Should -Be "test" + $res3.CredentialInfo["VaultName"] | Should -Be "test" + $res3.CredentialInfo["Secret"] | Should -Be "test" $res4 = Get-PSResourceRepository -Name $PSGalleryName $res4.Name | Should -Be $PSGalleryName $res4.URL | Should -Contain $PSGalleryURL $res4.Priority | Should -Be 50 $res4.Trusted | Should -Be True - $res4.Authentication | Should -BeNullOrEmpty + $res4.CredentialInfo | Should -BeNullOrEmpty } It "not set and throw error for trying to set PSGallery URL (NameParameterSet)" { @@ -165,10 +165,10 @@ Describe "Test Set-PSResourceRepository" { {Set-PSResourceRepository -Name "PSGallery" -URL $tmpDir1Path -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" } - It "not set and throw error for trying to set PSGallery Authentication (NameParameterSet)" { + It "not set and throw error for trying to set PSGallery CredentialInfo (NameParameterSet)" { Unregister-PSResourceRepository -Name "PSGallery" Register-PSResourceRepository -PSGallery - {Set-PSResourceRepository -Name "PSGallery" -Authentication @{VaultName = "test"; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + {Set-PSResourceRepository -Name "PSGallery" -CredentialInfo @{VaultName = "test"; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" } It "not set repository and throw error for trying to set PSGallery URL (RepositoriesParameterSet)" { @@ -201,13 +201,13 @@ Describe "Test Set-PSResourceRepository" { $res.Priority | Should -Be 50 } - It "not set repository and throw error for trying to set PSGallery Authentication (RepositoriesParameterSet)" { + It "not set repository and throw error for trying to set PSGallery CredentialInfo (RepositoriesParameterSet)" { Unregister-PSResourceRepository -Name "PSGallery" Register-PSResourceRepository -PSGallery Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path - $hashtable1 = @{Name = "PSGallery"; Authentication = @{VaultName = "test"; Secret = "test"}} + $hashtable1 = @{Name = "PSGallery"; CredentialInfo = @{VaultName = "test"; Secret = "test"}} $hashtable2 = @{Name = "testRepository"; Priority = 25} $arrayOfHashtables = $hashtable1, $hashtable2 @@ -219,25 +219,25 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 25 $res.Trusted | Should -Be False - $res.Authentication | Should -BeNullOrEmpty + $res.CredentialInfo | Should -BeNullOrEmpty } - It "not set and throw error if Authentication is invalid (NameParameterSet)" { + It "not set and throw error if CredentialInfo is invalid (NameParameterSet)" { Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path - {Set-PSResourceRepository -Name "testRepository" -Authentication @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" - {Set-PSResourceRepository -Name "testRepository" -Authentication @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + {Set-PSResourceRepository -Name "testRepository" -CredentialInfo @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + {Set-PSResourceRepository -Name "testRepository" -CredentialInfo @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" } - It "not set and throw error if Authentication is invalid (RepositoriesParameterSet)" { + It "not set and throw error if CredentialInfo is invalid (RepositoriesParameterSet)" { Register-PSResourceRepository -Name "testRepository1" -URL $tmpDir1Path Register-PSResourceRepository -Name "testRepository2" -URL $tmpDir2Path Register-PSResourceRepository -Name "testRepository3" -URL $tmpDir3Path Register-PSResourceRepository -Name "testRepository4" -URL $tmpDir4Path $hashtable1 = @{Name = "testRepository1"; Priority = 25} - $hashtable2 = @{Name = "testRepository2"; Authentication = @{Secret = ""}} - $hashtable3 = @{Name = "testRepository3"; Authentication = @{VaultName = "test"; Secret = ""}} - $hashtable4 = @{Name = "testRepository4"; Authentication = @{}} + $hashtable2 = @{Name = "testRepository2"; CredentialInfo = @{Secret = ""}} + $hashtable3 = @{Name = "testRepository3"; CredentialInfo = @{VaultName = "test"; Secret = ""}} + $hashtable4 = @{Name = "testRepository4"; CredentialInfo = @{}} $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 Set-PSResourceRepository -Repositories $arrayOfHashtables -ErrorVariable err -ErrorAction SilentlyContinue @@ -248,15 +248,15 @@ Describe "Test Set-PSResourceRepository" { $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 25 $res.Trusted | Should -Be False - $res.Authentication | Should -BeNullOrEmpty + $res.CredentialInfo | Should -BeNullOrEmpty $res2 = Get-PSResourceRepository -Name "testRepository2" - $res2.Authentication | Should -BeNullOrEmpty + $res2.CredentialInfo | Should -BeNullOrEmpty $res3 = Get-PSResourceRepository -Name "testRepository3" - $res3.Authentication | Should -BeNullOrEmpty + $res3.CredentialInfo | Should -BeNullOrEmpty $res4 = Get-PSResourceRepository -Name "testRepository4" - $res4.Authentication | Should -BeNullOrEmpty + $res4.CredentialInfo | Should -BeNullOrEmpty } } diff --git a/test/testRepositoriesWithAuthentication.xml b/test/testRepositoriesWithCredentialInfo.xml similarity index 100% rename from test/testRepositoriesWithAuthentication.xml rename to test/testRepositoriesWithCredentialInfo.xml From f5a4cf0157229603b3e5594c4f90b02c992121fe Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Thu, 30 Dec 2021 21:21:49 -0600 Subject: [PATCH 10/20] replace type Hashtable with PSCredentialInfo for the CredentialInfo parameter, update tests --- src/code/FindHelper.cs | 9 +- src/code/InstallHelper.cs | 9 +- src/code/PSCredentialInfo.cs | 105 ++++++++++++++++++ src/code/PSRepositoryInfo.cs | 8 +- src/code/RegisterPSResourceRepository.cs | 34 +++--- src/code/RepositorySettings.cs | 111 ++++++++++---------- src/code/SetPSResourceRepository.cs | 29 +++-- src/code/Utils.cs | 70 +++++++++++- test/PSCredentialInfo.Tests.ps1 | 82 +++++++++++++++ test/RegisterPSResourceRepository.Tests.ps1 | 55 +++++----- test/SetPSResourceRepository.Tests.ps1 | 64 ++++------- test/testRepositoriesWithCredentialInfo.xml | 4 +- 12 files changed, 404 insertions(+), 176 deletions(-) create mode 100644 src/code/PSCredentialInfo.cs create mode 100644 test/PSCredentialInfo.Tests.ps1 diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index 6e72e68fa..7a7e38f1c 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -197,7 +197,7 @@ public IEnumerable FindByResourceName( private IEnumerable SearchFromRepository( string repositoryName, Uri repositoryUrl, - Hashtable repositoryCredentialInfo) + PSCredentialInfo repositoryCredentialInfo) { PackageSearchResource resourceSearch; PackageMetadataResource resourceMetadata; @@ -244,12 +244,13 @@ private IEnumerable SearchFromRepository( else if (repositoryCredentialInfo != null) { var authHelper = new CredentialInfoHelper(_cmdletPassedIn); + // TODO: Change this to return PSCredential string password = authHelper.GetRepositoryCredentialInfoPassword( repositoryName, - repositoryCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString(), - repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString()); + repositoryCredentialInfo.VaultName, + repositoryCredentialInfo.SecretName); - source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString(), password, true, null); + source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repositoryCredentialInfo.SecretName, password, true, null); _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repositoryName); } diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index f9dbc5e01..da263cd51 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -283,7 +283,7 @@ private List InstallPackage( IEnumerable pkgsToInstall, string repoName, string repoUrl, - Hashtable repositoryCredentialInfo, + PSCredentialInfo repositoryCredentialInfo, PSCredential credential, bool isLocalRepo) { @@ -377,12 +377,13 @@ private List InstallPackage( else if (repositoryCredentialInfo != null) { var authHelper = new CredentialInfoHelper(_cmdletPassedIn); + // TODO: Change this to return PSCredential string password = authHelper.GetRepositoryCredentialInfoPassword( repoName, - repositoryCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString(), - repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString()); + repositoryCredentialInfo.VaultName, + repositoryCredentialInfo.SecretName); - source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repositoryCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString(), password, true, null); + source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repositoryCredentialInfo.SecretName, password, true, null); } var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider); SourceRepository repository = new SourceRepository(source, provider); diff --git a/src/code/PSCredentialInfo.cs b/src/code/PSCredentialInfo.cs new file mode 100644 index 000000000..f9b8926d6 --- /dev/null +++ b/src/code/PSCredentialInfo.cs @@ -0,0 +1,105 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; +using System.Management.Automation; + +namespace Microsoft.PowerShell.PowerShellGet.UtilClasses +{ + /// + /// This class contains information for a repository's authentication credential. + /// + public sealed class PSCredentialInfo + { + #region Constructor + + /// + /// Initializes a new instance of the PSCredentialInfo class with + /// vaultName and secretName of type string, and + /// (optionally) credential of type PSCredential. + /// + /// + /// + /// + public PSCredentialInfo(string vaultName, string secretName, PSCredential credential = null) + { + VaultName = vaultName; + SecretName = secretName; + Credential = credential; + } + + /// + /// Initializes a new instance of the PSCredentialInfo class with + /// vaultName and secretName of type string, and + /// (optionally) credential of type PSCredential from a PSObject. + /// + /// + public PSCredentialInfo(PSObject psObject) + { + if(psObject == null) + { + throw new ArgumentNullException(nameof(psObject)); + } + + VaultName = (string) psObject.Properties[PSCredentialInfo.VaultNameAttribute]?.Value; + SecretName = (string) psObject.Properties[PSCredentialInfo.SecretNameAttribute]?.Value; + Credential = (PSCredential) psObject.Properties[PSCredentialInfo.CredentialAttribute]?.Value; + } + + #endregion + + #region Members + + private string _vaultName; + /// + /// the Name of the SecretManagement Vault + /// + public string VaultName { + get + { + return _vaultName; + } + private set + { + if(string.IsNullOrEmpty(value)) + { + throw new ArgumentException($"Invalid CredentialInfo, {PSCredentialInfo.VaultNameAttribute} must be a non-empty string"); + } + + _vaultName = value; + } + } + + private string _secretName; + /// + /// the Name of the Secret + /// + public string SecretName { + get + { + return _secretName; + } + private set + { + if(string.IsNullOrEmpty(value)) + { + throw new ArgumentException($"Invalid CredentialInfo, {PSCredentialInfo.SecretNameAttribute} must be a non-empty string"); + } + + _secretName = value; + } + } + + /// + /// optional Credential object to save in a SecretManagement Vault + /// for authenticating to repositories + /// + public PSCredential Credential { get; private set; } + + internal static readonly string VaultNameAttribute = nameof(VaultName); + internal static readonly string SecretNameAttribute = nameof(SecretName); + internal static readonly string CredentialAttribute = nameof(Credential); + + #endregion + } +} diff --git a/src/code/PSRepositoryInfo.cs b/src/code/PSRepositoryInfo.cs index c84819dc0..294559c72 100644 --- a/src/code/PSRepositoryInfo.cs +++ b/src/code/PSRepositoryInfo.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Collections; using System.Management.Automation; namespace Microsoft.PowerShell.PowerShellGet.UtilClasses @@ -14,7 +13,7 @@ public sealed class PSRepositoryInfo { #region Constructor - public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, Hashtable credentialInfo) + public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, PSCredentialInfo credentialInfo) { Name = name; Url = url; @@ -39,6 +38,7 @@ public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, Hashta /// /// whether the repository is trusted + /// public bool Trusted { get; } /// @@ -48,9 +48,9 @@ public PSRepositoryInfo(string name, Uri url, int priority, bool trusted, Hashta public int Priority { get; } /// - /// the CredentialInfo for the repository + /// the credential information for repository authentication /// - public Hashtable CredentialInfo { get; } + public PSCredentialInfo CredentialInfo { get; } #endregion } diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index 553dd0da6..6f73d0365 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -85,11 +85,10 @@ class RegisterPSResourceRepository : PSCmdlet public int Priority { get; set; } = defaultPriority; /// - /// Specifies a hashtable of vault and secret names as CredentialInfo for the repository. + /// Specifies vault and secret names as PSCredentialInfo for the repository. /// [Parameter(ParameterSetName = NameParameterSet)] - [ValidateNotNullOrEmpty] - public Hashtable CredentialInfo { get; set; } + public PSCredentialInfo CredentialInfo { get; set; } /// /// Specifies a proxy server for the request, rather than a direct connection to the internet resource. @@ -201,7 +200,7 @@ protected override void ProcessRecord() } } - private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) + private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, PSCredentialInfo repoCredentialInfo) { // remove trailing and leading whitespaces, and if Name is just whitespace Name should become null now and be caught by following condition repoName = repoName.Trim(' '); @@ -215,13 +214,9 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl throw new ArgumentException("Invalid url, must be one of the following Uri schemes: HTTPS, HTTP, FTP, File Based"); } - if (repoCredentialInfo != null) + if (repoCredentialInfo?.Credential != null) { - if (!repoCredentialInfo.ContainsKey(CredentialInfoHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString()) - || !repoCredentialInfo.ContainsKey(CredentialInfoHelper.SecretAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString())) - { - throw new ArgumentException($"Invalid CredentialInfo, must include {CredentialInfoHelper.VaultNameAttribute} and {CredentialInfoHelper.SecretAttribute} key/(non-empty) value pairs"); - } + // TODO: Try adding credential to vault, throw terminating error if vault is inaccessible, etc. } WriteVerbose("All required values to add to repository provided, calling internal Add() API now"); @@ -233,7 +228,7 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl return RepositorySettings.Add(repoName, repoUrl, repoPriority, repoTrusted, repoCredentialInfo); } - private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) + private PSRepositoryInfo NameParameterSetHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, PSCredentialInfo repoCredentialInfo) { if (repoName.Equals("PSGallery", StringComparison.OrdinalIgnoreCase)) { @@ -338,17 +333,14 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) return null; } - Hashtable repoCredentialInfo = repo["CredentialInfo"] as Hashtable; - if (repoCredentialInfo != null) - { - if (!repoCredentialInfo.ContainsKey(CredentialInfoHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString()) - || !repoCredentialInfo.ContainsKey(CredentialInfoHelper.SecretAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString())) + PSCredentialInfo repoCredentialInfo = null; + if(repo.ContainsKey("CredentialInfo")) { + if (!Utils.TryCreateValidPSCredentialInfo(credentialInfoCandidate: (PSObject) repo["CredentialInfo"], + cmdletPassedIn: this, + repoCredentialInfo: out repoCredentialInfo, + errorRecord: out ErrorRecord errorRecord1)) { - WriteError(new ErrorRecord( - new PSInvalidOperationException($"Invalid CredentialInfo, must include {CredentialInfoHelper.VaultNameAttribute} and {CredentialInfoHelper.SecretAttribute} key/(non-empty) value pairs"), - "InvalidCredentialInfo", - ErrorCategory.InvalidArgument, - this)); + WriteError(errorRecord1); return null; } } diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs index 8e1eb1d1e..a9923570d 100644 --- a/src/code/RepositorySettings.cs +++ b/src/code/RepositorySettings.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.IO; @@ -32,9 +31,6 @@ internal static class RepositorySettings Environment.SpecialFolder.LocalApplicationData), "PowerShellGet"); private static readonly string FullRepositoryPath = Path.Combine(RepositoryPath, RepositoryFileName); - private static readonly string VaultNameAttribute = "VaultName"; - private static readonly string SecretAttribute = "Secret"; - #endregion #region Public methods @@ -83,7 +79,7 @@ public static void CheckRepositoryStore() /// Returns: PSRepositoryInfo containing information about the repository just added to the repository store /// /// - public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) + public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriority, bool repoTrusted, PSCredentialInfo repoCredentialInfo) { try { @@ -108,8 +104,8 @@ public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriorit ); if(repoCredentialInfo != null) { - newElement.Add(new XAttribute(VaultNameAttribute, repoCredentialInfo[VaultNameAttribute])); - newElement.Add(new XAttribute(SecretAttribute, repoCredentialInfo[SecretAttribute])); + newElement.Add(new XAttribute(PSCredentialInfo.VaultNameAttribute, repoCredentialInfo.VaultName)); + newElement.Add(new XAttribute(PSCredentialInfo.SecretNameAttribute, repoCredentialInfo.SecretName)); } root.Add(newElement); @@ -126,10 +122,10 @@ public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriorit } /// - /// Updates a repository name, URL, priority, or installation policy + /// Updates a repository name, URL, priority, installation policy, or credential information /// Returns: void /// - public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPriority, bool? repoTrusted, Hashtable repoCredentialInfo) + public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPriority, bool? repoTrusted, PSCredentialInfo repoCredentialInfo) { PSRepositoryInfo updatedRepo; try @@ -168,21 +164,21 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio } // A null CredentialInfo value passed in signifies that CredentialInfo was not attempted to be set. - // So only set VaultName and Secret attributes if non-null value passed in for repoCredentialInfo + // So only set VaultName and SecretName attributes if non-null value passed in for repoCredentialInfo if (repoCredentialInfo != null) { - if (node.Attribute(VaultNameAttribute) == null) { - node.Add(new XAttribute(VaultNameAttribute, repoCredentialInfo[VaultNameAttribute])); + if (node.Attribute(PSCredentialInfo.VaultNameAttribute) == null) { + node.Add(new XAttribute(PSCredentialInfo.VaultNameAttribute, repoCredentialInfo.VaultName)); } else { - node.Attribute(VaultNameAttribute).Value = repoCredentialInfo[VaultNameAttribute].ToString(); + node.Attribute(PSCredentialInfo.VaultNameAttribute).Value = repoCredentialInfo.VaultName; } - if (node.Attribute(SecretAttribute) == null) { - node.Add(new XAttribute(SecretAttribute, repoCredentialInfo[SecretAttribute])); + if (node.Attribute(PSCredentialInfo.SecretNameAttribute) == null) { + node.Add(new XAttribute(PSCredentialInfo.SecretNameAttribute, repoCredentialInfo.SecretName)); } else { - node.Attribute(SecretAttribute).Value = repoCredentialInfo[SecretAttribute].ToString(); + node.Attribute(PSCredentialInfo.SecretNameAttribute).Value = repoCredentialInfo.SecretName; } } @@ -193,12 +189,15 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio } // Create CredentialInfo based on new values or whether it was empty to begin with - Hashtable thisCredentialInfo = !string.IsNullOrEmpty(node.Attribute(VaultNameAttribute)?.Value) && !string.IsNullOrEmpty(node.Attribute(SecretAttribute)?.Value) - ? new Hashtable() { - { VaultNameAttribute, node.Attribute(VaultNameAttribute).Value }, - { SecretAttribute, node.Attribute(SecretAttribute).Value } - } - : null; + PSCredentialInfo thisCredentialInfo; + try + { + thisCredentialInfo = new PSCredentialInfo(node.Attribute(PSCredentialInfo.VaultNameAttribute).Value, node.Attribute(PSCredentialInfo.SecretNameAttribute).Value); + } + catch (Exception) + { + thisCredentialInfo = null; + } updatedRepo = new PSRepositoryInfo(repoName, thisUrl, @@ -285,30 +284,32 @@ public static List Read(string[] repoNames, out string[] error continue; } - Hashtable thisCredentialInfo = null; - string authErrorMessage = $"Repository {repo.Attribute("Name")} has invalid CredentialInfo. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; - // both keys present - if (repo.Attribute(VaultNameAttribute) != null && repo.Attribute(SecretAttribute) != null) { - // both values non-empty - // = valid credentialInfo - if (!string.IsNullOrEmpty(repo.Attribute(VaultNameAttribute).Value) && !string.IsNullOrEmpty(repo.Attribute(SecretAttribute).Value)) { - thisCredentialInfo = new Hashtable() { - { VaultNameAttribute, repo.Attribute(VaultNameAttribute).Value }, - { SecretAttribute, repo.Attribute(SecretAttribute).Value } - }; + PSCredentialInfo thisCredentialInfo; + string credentialInfoErrorMessage = $"Repository {repo.Attribute("Name").Value} has invalid CredentialInfo. {PSCredentialInfo.VaultNameAttribute} and {PSCredentialInfo.SecretNameAttribute} should both be present and non-empty"; + // both keys are present + if (repo.Attribute(PSCredentialInfo.VaultNameAttribute) != null && repo.Attribute(PSCredentialInfo.SecretNameAttribute) != null) { + try + { + // both values are non-empty + // = valid credentialInfo + thisCredentialInfo = new PSCredentialInfo(repo.Attribute(PSCredentialInfo.VaultNameAttribute).Value, repo.Attribute(PSCredentialInfo.SecretNameAttribute).Value); } - else { - tempErrorList.Add(authErrorMessage); + catch (Exception) + { + thisCredentialInfo = null; + tempErrorList.Add(credentialInfoErrorMessage); continue; } } // both keys are missing - else if (repo.Attribute(VaultNameAttribute) == null && repo.Attribute(SecretAttribute) == null) { - // = valid credentialInfo, do nothing + else if (repo.Attribute(PSCredentialInfo.VaultNameAttribute) == null && repo.Attribute(PSCredentialInfo.SecretNameAttribute) == null) { + // = valid credentialInfo + thisCredentialInfo = null; } // one of the keys is missing else { - tempErrorList.Add(authErrorMessage); + thisCredentialInfo = null; + tempErrorList.Add(credentialInfoErrorMessage); continue; } @@ -338,30 +339,32 @@ public static List Read(string[] repoNames, out string[] error continue; } - Hashtable thisCredentialInfo = null; - string authErrorMessage = $"Repository {node.Attribute("Name")} has invalid CredentialInfo. {VaultNameAttribute} and {SecretAttribute} should both be present and non-empty"; - // both keys present - if (node.Attribute(VaultNameAttribute) != null && node.Attribute(SecretAttribute) != null) { - // both values non-empty - // = valid credentialInfo - if (!string.IsNullOrEmpty(node.Attribute(VaultNameAttribute).Value) && !string.IsNullOrEmpty(node.Attribute(SecretAttribute).Value)) { - thisCredentialInfo = new Hashtable() { - { VaultNameAttribute, node.Attribute(VaultNameAttribute).Value }, - { SecretAttribute, node.Attribute(SecretAttribute).Value } - }; + PSCredentialInfo thisCredentialInfo; + string credentialInfoErrorMessage = $"Repository {node.Attribute("Name").Value} has invalid CredentialInfo. {PSCredentialInfo.VaultNameAttribute} and {PSCredentialInfo.SecretNameAttribute} should both be present and non-empty"; + // both keys are present + if (node.Attribute(PSCredentialInfo.VaultNameAttribute) != null && node.Attribute(PSCredentialInfo.SecretNameAttribute) != null) { + try + { + // both values are non-empty + // = valid credentialInfo + thisCredentialInfo = new PSCredentialInfo(node.Attribute(PSCredentialInfo.VaultNameAttribute).Value, node.Attribute(PSCredentialInfo.SecretNameAttribute).Value); } - else { - tempErrorList.Add(authErrorMessage); + catch (Exception) + { + thisCredentialInfo = null; + tempErrorList.Add(credentialInfoErrorMessage); continue; } } // both keys are missing - else if (node.Attribute(VaultNameAttribute) == null && node.Attribute(SecretAttribute) == null) { - // = valid credentialInfo, do nothing + else if (node.Attribute(PSCredentialInfo.VaultNameAttribute) == null && node.Attribute(PSCredentialInfo.SecretNameAttribute) == null) { + // = valid credentialInfo + thisCredentialInfo = null; } // one of the keys is missing else { - tempErrorList.Add(authErrorMessage); + thisCredentialInfo = null; + tempErrorList.Add(credentialInfoErrorMessage); continue; } diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs index baf997e27..84b601a2d 100644 --- a/src/code/SetPSResourceRepository.cs +++ b/src/code/SetPSResourceRepository.cs @@ -83,11 +83,10 @@ public SwitchParameter Trusted public int Priority { get; set; } = DefaultPriority; /// - /// Specifies a hashtable of vault and secret names as CredentialInfo for the repository. + /// Specifies vault and secret names as PSCredentialInfo for the repository. /// [Parameter(ParameterSetName = NameParameterSet)] - [ValidateNotNullOrEmpty] - public Hashtable CredentialInfo {get; set;} + public PSCredentialInfo CredentialInfo { get; set; } /// /// When specified, displays the successfully registered repository and its information @@ -163,7 +162,7 @@ protected override void ProcessRecord() } } - private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, Hashtable repoCredentialInfo) + private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUrl, int repoPriority, bool repoTrusted, PSCredentialInfo repoCredentialInfo) { if (repoUrl != null && !(repoUrl.Scheme == Uri.UriSchemeHttp || repoUrl.Scheme == Uri.UriSchemeHttps || repoUrl.Scheme == Uri.UriSchemeFtp || repoUrl.Scheme == Uri.UriSchemeFile)) { @@ -193,13 +192,9 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr // determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable) bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?(); - if (repoCredentialInfo != null) + if (repoCredentialInfo?.Credential != null) { - if (!repoCredentialInfo.ContainsKey(CredentialInfoHelper.VaultNameAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.VaultNameAttribute].ToString()) - || !repoCredentialInfo.ContainsKey(CredentialInfoHelper.SecretAttribute) || string.IsNullOrEmpty(repoCredentialInfo[CredentialInfoHelper.SecretAttribute].ToString())) - { - throw new ArgumentException($"Invalid CredentialInfo, must include {CredentialInfoHelper.VaultNameAttribute} and {CredentialInfoHelper.SecretAttribute} key/(non-empty) value pairs"); - } + // TODO: Try adding credential to vault, throw terminating error if vault is inaccessible, etc. } // determine if either 1 of 4 values are attempting to be set: URL, Priority, Trusted, CredentialInfo. @@ -276,13 +271,25 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) isSet = true; } + PSCredentialInfo repoCredentialInfo = null; + if(repo.ContainsKey("CredentialInfo")) { + if (!Utils.TryCreateValidPSCredentialInfo(credentialInfoCandidate: (PSObject) repo["CredentialInfo"], + cmdletPassedIn: this, + repoCredentialInfo: out repoCredentialInfo, + errorRecord: out ErrorRecord errorRecord1)) + { + WriteError(errorRecord1); + return null; + } + } + try { return UpdateRepositoryStoreHelper(repo["Name"].ToString(), repoURL, repo.ContainsKey("Priority") ? Convert.ToInt32(repo["Priority"].ToString()) : DefaultPriority, repoTrusted, - repo["CredentialInfo"] as Hashtable); + repoCredentialInfo); } catch (Exception e) { diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 6d6496fdb..7b80bf999 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -265,7 +265,75 @@ public static bool TryCreateValidUrl( } #endregion - + + #region PSCredentialInfo methods + + public static bool TryCreateValidPSCredentialInfo( + PSObject credentialInfoCandidate, + PSCmdlet cmdletPassedIn, + out PSCredentialInfo repoCredentialInfo, + out ErrorRecord errorRecord) + { + repoCredentialInfo = null; + errorRecord = null; + + try + { + if(!string.IsNullOrEmpty((string) credentialInfoCandidate.Properties[PSCredentialInfo.VaultNameAttribute]?.Value) + && !string.IsNullOrEmpty((string) credentialInfoCandidate.Properties[PSCredentialInfo.SecretNameAttribute]?.Value)) + { + PSCredential credential = null; + if(credentialInfoCandidate.Properties[PSCredentialInfo.CredentialAttribute] != null) + { + try + { + credential = (PSCredential) credentialInfoCandidate.Properties[PSCredentialInfo.CredentialAttribute].Value; + } + catch (Exception e) + { + errorRecord = new ErrorRecord( + new PSArgumentException($"Invalid CredentialInfo {PSCredentialInfo.CredentialAttribute}", e), + "InvalidCredentialInfo", + ErrorCategory.InvalidArgument, + cmdletPassedIn); + + return false; + } + } + + repoCredentialInfo = new PSCredentialInfo( + (string) credentialInfoCandidate.Properties[PSCredentialInfo.VaultNameAttribute].Value, + (string) credentialInfoCandidate.Properties[PSCredentialInfo.SecretNameAttribute].Value, + credential + ); + + return true; + } + else + { + errorRecord = new ErrorRecord( + new PSArgumentException($"Invalid CredentialInfo, must include non-empty {PSCredentialInfo.VaultNameAttribute} and {PSCredentialInfo.SecretNameAttribute}, and optionally a {PSCredentialInfo.CredentialAttribute}"), + "InvalidCredentialInfo", + ErrorCategory.InvalidArgument, + cmdletPassedIn); + + return false; + } + } + catch (Exception e) + { + errorRecord = new ErrorRecord( + new PSArgumentException("Invalid CredentialInfo values", e), + "InvalidCredentialInfo", + ErrorCategory.InvalidArgument, + cmdletPassedIn); + + return false; + } + } + + #endregion + #region Path methods public static string[] GetSubDirectories(string dirPath) diff --git a/test/PSCredentialInfo.Tests.ps1 b/test/PSCredentialInfo.Tests.ps1 new file mode 100644 index 000000000..13d549a56 --- /dev/null +++ b/test/PSCredentialInfo.Tests.ps1 @@ -0,0 +1,82 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force + +Describe "Create PSCredentialInfo with VaultName and SecretName" -tags 'CI' { + + It "Verifies VaultName is not empty" { + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("", "testsecret") } | Should -Throw "Invalid CredentialInfo, VaultName must be a non-empty string" + } + + It "Verifies SecretName is not empty" { + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "") } | Should -Throw "Invalid CredentialInfo, SecretName must be a non-empty string" + } + + It "Creates PSCredentialInfo successfully if VaultName and SecretName are non-empty" { + $credentialInfo = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret") + $credentialInfo.VaultName | Should -Be "testvault" + $credentialInfo.SecretName | Should -Be "testsecret" + } +} + +Describe "Create PSCredentialInfo with VaultName, SecretName, and Credential" -tags 'CI' { + + It "Creates PSCredentialInfo successfully if Credential is null" { + $credentialInfo = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $null) + + $credentialInfo.VaultName | Should -Be "testvault" + $credentialInfo.SecretName | Should -Be "testsecret" + } + + It "Creates PSCredentialInfo successfully if Credential is non-null and of type PSCredential" { + $credential = New-Object System.Management.Automation.PSCredential ("username", (ConvertTo-SecureString "password" -AsPlainText -Force)) + $credentialInfo = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $credential) + + $credentialInfo.VaultName | Should -Be "testvault" + $credentialInfo.SecretName | Should -Be "testsecret" + } +} + +Describe "Create PSCredentialInfo from a PSObject" -tags 'CI' { + + It "Throws if VaultName is null" { + $customObject = New-Object PSObject + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo $customObject } | Should -Throw "Invalid CredentialInfo, VaultName must be a non-empty string" + } + + It "Throws if SecretName is null" { + $customObject = New-Object PSObject + $customObject | Add-Member -Name "VaultName" -Value "testvault" -MemberType NoteProperty + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo $customObject } | Should -Throw "Invalid CredentialInfo, SecretName must be a non-empty string" + } + + It "Creates PSCredentialInfo successfully from PSObject with VaultName and SecretName" { + $properties = [PSCustomObject]@{ + VaultName = "testvault" + SecretName = "testsecret" + } + + $credentialInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo] $properties + + $credentialInfo.VaultName | Should -Be "testvault" + $credentialInfo.SecretName | Should -Be "testsecret" + } + + It "Creates PSCredentialInfo successfully from PSObject with VaultName, SecretName and Credential" { + $credential = New-Object System.Management.Automation.PSCredential ("username", (ConvertTo-SecureString "password" -AsPlainText -Force)) + $properties = [PSCustomObject]@{ + VaultName = "testvault" + SecretName = "testsecret" + Credential = [PSCredential] $credential + } + + $credentialInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo] $properties + + $credentialInfo.VaultName | Should -Be "testvault" + $credentialInfo.SecretName | Should -Be "testsecret" + $credentialInfo.Credential.UserName | Should -Be "username" + $credentialInfo.Credential.GetNetworkCredential().Password | Should -Be "password" + + } +} \ No newline at end of file diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index 23dd08d89..1888c6262 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -16,6 +16,11 @@ Describe "Test Register-PSResourceRepository" { Get-NewTestDirs($tmpDirPaths) $relativeCurrentPath = Get-Location + + $credentialInfo1 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret") + $secureString = ConvertTo-SecureString "testpassword" -AsPlainText -Force + $credential = New-Object System.Management.Automation.PSCredential ("testusername", $secureString) + $credentialInfo2 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $credential) } AfterEach { Get-RevertPSResourceRepositoryFile @@ -52,13 +57,15 @@ Describe "Test Register-PSResourceRepository" { } It "register repository given Name, URL, Trusted, Priority, CredentialInfo (NameParameterSet)" { - $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo @{VaultName = "testvault"; Secret = "testsecret"} -PassThru + $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo2 -PassThru $res.Name | Should -Be "testRepository" $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Trusted | Should -Be True $res.Priority | Should -Be 20 - $res.CredentialInfo["VaultName"] | Should -Be "testvault" - $res.CredentialInfo["Secret"] | Should -Be "testsecret" + $res.CredentialInfo.VaultName | Should -Be "testvault" + $res.CredentialInfo.SecretName | Should -Be "testsecret" + $res.CredentialInfo.Credential | Should -Not -BeNullOrEmpty + $res.CredentialInfo.Credential.UserName | Should -Be "testusername" } It "register repository with PSGallery parameter (PSGalleryParameterSet)" { @@ -92,7 +99,7 @@ Describe "Test Register-PSResourceRepository" { $hashtable1 = @{Name = "testRepository"; URL = $tmpDir1Path} $hashtable2 = @{Name = "testRepository2"; URL = $tmpDir2Path; Trusted = $True} $hashtable3 = @{Name = "testRepository3"; URL = $tmpDir3Path; Trusted = $True; Priority = 20} - $hashtable4 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = @{VaultName = "testvault"; Secret = "testsecret"}} + $hashtable4 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = (New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret"))} $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 Register-PSResourceRepository -Repositories $arrayOfHashtables @@ -115,8 +122,9 @@ Describe "Test Register-PSResourceRepository" { $res4.URL.LocalPath | Should -Contain $tmpDir4Path $res4.Trusted | Should -Be True $res4.Priority | Should -Be 30 - $res4.CredentialInfo["VaultName"] | Should -Be "testvault" - $res4.CredentialInfo["Secret"] | Should -Be "testsecret" + $res4.CredentialInfo.VaultName | Should -Be "testvault" + $res4.CredentialInfo.SecretName | Should -Be "testsecret" + $res4.CredentialInfo.Credential | Should -BeNullOrEmpty } It "register repositories with Repositories parameter, psgallery style repository (RepositoriesParameterSet)" { @@ -135,7 +143,7 @@ Describe "Test Register-PSResourceRepository" { $hashtable2 = @{Name = "testRepository"; URL = $tmpDir1Path} $hashtable3 = @{Name = "testRepository2"; URL = $tmpDir2Path; Trusted = $True} $hashtable4 = @{Name = "testRepository3"; URL = $tmpDir3Path; Trusted = $True; Priority = 20} - $hashtable5 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = @{VaultName = "testvault"; Secret = "testsecret"}} + $hashtable5 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = (New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", (New-Object System.Management.Automation.PSCredential ("testusername", (ConvertTo-SecureString "testpassword" -AsPlainText -Force)))))} $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4, $hashtable5 Register-PSResourceRepository -Repositories $arrayOfHashtables @@ -164,8 +172,11 @@ Describe "Test Register-PSResourceRepository" { $res5.URL.LocalPath | Should -Contain $tmpDir4Path $res5.Trusted | Should -Be True $res5.Priority | Should -Be 30 - $res5.CredentialInfo["VaultName"] | Should -Be "testvault" - $res5.CredentialInfo["Secret"] | Should -Be "testsecret" + $res5.CredentialInfo.VaultName | Should -Be "testvault" + $res5.CredentialInfo.SecretName | Should -Be "testsecret" + # Get-PSResourceRepository does not return Credential information as it is not saved + # TODO: update based on how username vs secretname is saved + $res5.CredentialInfo.Credential | Should -BeNullOrEmpty } It "not register repository when Name is provided but URL is not" { @@ -184,16 +195,6 @@ Describe "Test Register-PSResourceRepository" { {Register-PSResourceRepository -Name " " -URL $tmpDir1Path -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } - It "not register if CredentialInfo is missing VaultName or Secret" { - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - } - - It "not register if CredentialInfo values are empty" { - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - {Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -CredentialInfo @{VaultName = ""; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - } - It "not register PSGallery with NameParameterSet" { {Register-PSResourceRepository -Name $PSGalleryName -URL $PSGalleryURL -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } @@ -202,12 +203,12 @@ Describe "Test Register-PSResourceRepository" { It "not register PSGallery when PSGallery parameter provided with Name, URL or CredentialInfo" { {Register-PSResourceRepository -PSGallery -Name $PSGalleryName -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" {Register-PSResourceRepository -PSGallery -URL $PSGalleryURL -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" - {Register-PSResourceRepository -PSGallery -CredentialInfo @{VaultName = "testvault"; Secret = "testsecret"} -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -PSGallery -CredentialInfo $credentialInfo1 -ErrorAction Stop} | Should -Throw -ErrorId "AmbiguousParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } $testCases = @{Type = "Name key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; Name=$PSGalleryName}}, @{Type = "URL key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; URL=$PSGalleryURL}}, - @{Type = "CredentialInfo key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; CredentialInfo = @{VaultName = "test"; Secret = "test"}}} + @{Type = "CredentialInfo key specified with PSGallery key"; IncorrectHashTable = @{PSGallery = $True; CredentialInfo = $credentialInfo1}} It "not register incorrectly formatted PSGallery type repo among correct ones when incorrect type is " -TestCases $testCases { param($Type, $IncorrectHashTable) @@ -232,14 +233,10 @@ Describe "Test Register-PSResourceRepository" { $res3.Name | Should -Be "testRepository3" } - $testCases2 = @{Type = "-Name is not specified"; IncorrectHashTable = @{URL = $tmpDir1Path}; ErrorId = "NullNameForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-Name is PSGallery"; IncorrectHashTable = @{Name = "PSGallery"; URL = $tmpDir1Path}; ErrorId = "PSGalleryProvidedAsNameRepoPSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-URL not specified"; IncorrectHashTable = @{Name = "testRepository"}; ErrorId = "NullURLForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-URL is not valid scheme"; IncorrectHashTable = @{Name = "testRepository"; URL="www.google.com"}; ErrorId = "InvalidUri,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-CredentialInfo is missing VaultName"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{Secret = "test"}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-CredentialInfo is missing Secret"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{VaultName = "test"}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-CredentialInfo-VaultName value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{VaultName = ""; Secret = "test"}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-CredentialInfo-Secret value is empty"; IncorrectHashTable = @{Name = "testRepository"; URL=$tmpDir1Path; CredentialInfo = @{VaultName = "test"; Secret = ""}}; ErrorId = "InvalidCredentialInfo,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} + $testCases2 = @{Type = "-Name is not specified"; IncorrectHashTable = @{URL = $tmpDir1Path}; ErrorId = "NullNameForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-Name is PSGallery"; IncorrectHashTable = @{Name = "PSGallery"; URL = $tmpDir1Path}; ErrorId = "PSGalleryProvidedAsNameRepoPSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-URL not specified"; IncorrectHashTable = @{Name = "testRepository"}; ErrorId = "NullURLForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-URL is not valid scheme"; IncorrectHashTable = @{Name = "testRepository"; URL="www.google.com"}; ErrorId = "InvalidUri,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} It "not register incorrectly formatted Name type repo among correct ones when incorrect type is " -TestCases $testCases2 { param($Type, $IncorrectHashTable, $ErrorId) diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 74d223bdb..9f4d852e7 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -16,6 +16,10 @@ Describe "Test Set-PSResourceRepository" { Get-NewTestDirs($tmpDirPaths) $relativeCurrentPath = Get-Location + + $secureString = ConvertTo-SecureString "testpassword" -AsPlainText -Force + $credential = New-Object System.Management.Automation.PSCredential ("testusername", $secureString) + $credentialInfo1 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $credential) } AfterEach { Get-RevertPSResourceRepositoryFile @@ -62,14 +66,17 @@ Describe "Test Set-PSResourceRepository" { It "set repository given Name and CredentialInfo parameters" { Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path - Set-PSResourceRepository -Name "testRepository" -CredentialInfo @{VaultName = "test"; Secret = "test"} + Set-PSResourceRepository -Name "testRepository" -CredentialInfo $credentialInfo1 $res = Get-PSResourceRepository -Name "testRepository" $res.Name | Should -Be "testRepository" $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False - $res.CredentialInfo["VaultName"] | Should -Be "test" - $res.CredentialInfo["Secret"] | Should -Be "test" + $res.CredentialInfo.VaultName | Should -Be "testvault" + $res.CredentialInfo.SecretName | Should -Be "testsecret" + # Get-PSResourceRepository does not return Credential information as it is not saved + # TODO: update based on how username vs secretname is saved + $res.CredentialInfo.Credential | Should -BeNullOrEmpty } It "not set repository and write error given just Name parameter" { @@ -124,7 +131,7 @@ Describe "Test Set-PSResourceRepository" { $hashtable1 = @{Name = "testRepository1"; URL = $tmpDir2Path}; $hashtable2 = @{Name = "testRepository2"; Priority = 25}; - $hashtable3 = @{Name = "testRepository3"; CredentialInfo = @{VaultName = "test"; Secret = "test"}}; + $hashtable3 = @{Name = "testRepository3"; CredentialInfo = [PSCustomObject] @{ VaultName = "testvault"; SecretName = "testsecret"; Credential = [PSCredential] $credential }}; $hashtable4 = @{Name = "PSGallery"; Trusted = $True}; $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 @@ -148,8 +155,11 @@ Describe "Test Set-PSResourceRepository" { $res3.URL.LocalPath | Should -Contain $tmpDir3Path $res3.Priority | Should -Be 50 $res3.Trusted | Should -Be False - $res3.CredentialInfo["VaultName"] | Should -Be "test" - $res3.CredentialInfo["Secret"] | Should -Be "test" + $res3.CredentialInfo.VaultName | Should -Be "testvault" + $res3.CredentialInfo.SecretName | Should -Be "testsecret" + # Get-PSResourceRepository does not return Credential information as it is not saved + # TODO: update based on how username vs secretname is saved + $res3.CredentialInfo.Credential | Should -BeNullOrEmpty $res4 = Get-PSResourceRepository -Name $PSGalleryName $res4.Name | Should -Be $PSGalleryName @@ -168,7 +178,7 @@ Describe "Test Set-PSResourceRepository" { It "not set and throw error for trying to set PSGallery CredentialInfo (NameParameterSet)" { Unregister-PSResourceRepository -Name "PSGallery" Register-PSResourceRepository -PSGallery - {Set-PSResourceRepository -Name "PSGallery" -CredentialInfo @{VaultName = "test"; Secret = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + {Set-PSResourceRepository -Name "PSGallery" -CredentialInfo $credentialInfo1 -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" } It "not set repository and throw error for trying to set PSGallery URL (RepositoriesParameterSet)" { @@ -207,7 +217,7 @@ Describe "Test Set-PSResourceRepository" { Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path - $hashtable1 = @{Name = "PSGallery"; CredentialInfo = @{VaultName = "test"; Secret = "test"}} + $hashtable1 = @{Name = "PSGallery"; CredentialInfo = $credentialInfo1} $hashtable2 = @{Name = "testRepository"; Priority = 25} $arrayOfHashtables = $hashtable1, $hashtable2 @@ -221,42 +231,4 @@ Describe "Test Set-PSResourceRepository" { $res.Trusted | Should -Be False $res.CredentialInfo | Should -BeNullOrEmpty } - - It "not set and throw error if CredentialInfo is invalid (NameParameterSet)" { - Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path - {Set-PSResourceRepository -Name "testRepository" -CredentialInfo @{VaultName = "test"} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" - {Set-PSResourceRepository -Name "testRepository" -CredentialInfo @{VaultName = "test"; Secret = ""} -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" - } - - It "not set and throw error if CredentialInfo is invalid (RepositoriesParameterSet)" { - Register-PSResourceRepository -Name "testRepository1" -URL $tmpDir1Path - Register-PSResourceRepository -Name "testRepository2" -URL $tmpDir2Path - Register-PSResourceRepository -Name "testRepository3" -URL $tmpDir3Path - Register-PSResourceRepository -Name "testRepository4" -URL $tmpDir4Path - - $hashtable1 = @{Name = "testRepository1"; Priority = 25} - $hashtable2 = @{Name = "testRepository2"; CredentialInfo = @{Secret = ""}} - $hashtable3 = @{Name = "testRepository3"; CredentialInfo = @{VaultName = "test"; Secret = ""}} - $hashtable4 = @{Name = "testRepository4"; CredentialInfo = @{}} - $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 - - Set-PSResourceRepository -Repositories $arrayOfHashtables -ErrorVariable err -ErrorAction SilentlyContinue - $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "ErrorSettingIndividualRepoFromRepositories,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" - - $res = Get-PSResourceRepository -Name "testRepository1" - $res.URL.LocalPath | Should -Contain $tmpDir1Path - $res.Priority | Should -Be 25 - $res.Trusted | Should -Be False - $res.CredentialInfo | Should -BeNullOrEmpty - - $res2 = Get-PSResourceRepository -Name "testRepository2" - $res2.CredentialInfo | Should -BeNullOrEmpty - - $res3 = Get-PSResourceRepository -Name "testRepository3" - $res3.CredentialInfo | Should -BeNullOrEmpty - - $res4 = Get-PSResourceRepository -Name "testRepository4" - $res4.CredentialInfo | Should -BeNullOrEmpty - } } diff --git a/test/testRepositoriesWithCredentialInfo.xml b/test/testRepositoriesWithCredentialInfo.xml index 1be96724f..2797e7bf4 100644 --- a/test/testRepositoriesWithCredentialInfo.xml +++ b/test/testRepositoriesWithCredentialInfo.xml @@ -1,7 +1,7 @@ - + - + From ce16394a95f7d7a8f8ffda1d0276f3648c43c858 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Wed, 5 Jan 2022 17:36:46 -0600 Subject: [PATCH 11/20] add save repository credential functionality and secret management error handling --- src/code/CredentialInfoHelper.cs | 194 ----------- src/code/FindHelper.cs | 8 +- src/code/InstallHelper.cs | 14 +- src/code/RegisterPSResourceRepository.cs | 25 +- src/code/SetPSResourceRepository.cs | 25 +- src/code/Utils.cs | 349 ++++++++++++++++++++ test/RegisterPSResourceRepository.Tests.ps1 | 11 +- test/SetPSResourceRepository.Tests.ps1 | 10 +- 8 files changed, 408 insertions(+), 228 deletions(-) delete mode 100644 src/code/CredentialInfoHelper.cs diff --git a/src/code/CredentialInfoHelper.cs b/src/code/CredentialInfoHelper.cs deleted file mode 100644 index ec3515b18..000000000 --- a/src/code/CredentialInfoHelper.cs +++ /dev/null @@ -1,194 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Collections.ObjectModel; -using System.Globalization; -using System.Management.Automation; -using System.Management.Automation.Runspaces; - -namespace Microsoft.PowerShell.PowerShellGet.Cmdlets -{ - /// - /// CredentialInfo helper class includes functions to get repository credentials from Microsoft.PowerShell.SecretManagement if provided - /// - internal class CredentialInfoHelper - { - internal static readonly string VaultNameAttribute = "VaultName"; - internal static readonly string SecretAttribute = "Secret"; - - private readonly PSCmdlet _cmdletPassedIn; - - private static readonly string SecretManagementModuleName = "Microsoft.PowerShell.SecretManagement"; - - public CredentialInfoHelper(PSCmdlet cmdletPassedIn) - { - _cmdletPassedIn = cmdletPassedIn; - } - - public string GetRepositoryCredentialInfoPassword(string repositoryName, string vaultName, string secretName) - { - var results = PowerShellInvoker.InvokeScriptWithHost( - cmdlet: _cmdletPassedIn, - script: $@" - param ( - [string] $VaultName, - [string] $SecretName - ) - $module = Microsoft.PowerShell.Core\Import-Module -Name {SecretManagementModuleName} -PassThru - if ($null -eq $module) {{ - return - }} - & $module ""Get-Secret"" -Name $SecretName -Vault $VaultName -AsPlainText - ", - args: new object[] { vaultName, secretName }, - out Exception terminatingError); - - string secretValueInPlainText = (results.Count == 1) ? results[0] : null; - // SecretStore allows empty secret values so only check for null - if (secretValueInPlainText == null) - { - _cmdletPassedIn.ThrowTerminatingError( - new ErrorRecord( - new PSInvalidOperationException( - message: string.Format(CultureInfo.InvariantCulture, "Unable to read secret {0} from vault {1} for authenticating to PSResourceRepository {2}", secretName, vaultName, repositoryName), - innerException: terminatingError), - "RepositoryCredentialInfoCannotGetSecretFromVault", - ErrorCategory.InvalidOperation, - this)); - } - - return secretValueInPlainText; - } - } - - #region PowerShellInvoker - - internal static class PowerShellInvoker - { - #region Members - - private static Runspace _runspace; - - #endregion Members - - #region Methods - - public static Collection InvokeScriptWithHost( - PSCmdlet cmdlet, - string script, - object[] args, - out Exception terminatingError) - { - Collection returnCollection = new Collection(); - terminatingError = null; - - if (_runspace == null || _runspace.RunspaceStateInfo.State != RunspaceState.Opened) - { - if (_runspace != null) - { - _runspace.Dispose(); - } - - var iss = InitialSessionState.CreateDefault2(); - // We are running trusted script. - iss.LanguageMode = PSLanguageMode.FullLanguage; - // Import the current PowerShellGet module. - var modPathObjects = cmdlet.InvokeCommand.InvokeScript( - script: "(Get-Module -Name PowerShellGet).Path"); - string modPath = (modPathObjects.Count > 0 && - modPathObjects[0].BaseObject is string modPathStr) - ? modPathStr : string.Empty; - if (!string.IsNullOrEmpty(modPath)) - { - iss.ImportPSModule(new string[] { modPath }); - } - - try - { - _runspace = RunspaceFactory.CreateRunspace(cmdlet.Host, iss); - _runspace.Open(); - } - catch (Exception ex) - { - terminatingError = ex; - return returnCollection; - } - } - - using (var ps = System.Management.Automation.PowerShell.Create()) - { - ps.Runspace = _runspace; - - var cmd = new Command( - command: script, - isScript: true, - useLocalScope: true); - cmd.MergeMyResults( - myResult: PipelineResultTypes.Error | PipelineResultTypes.Warning | PipelineResultTypes.Verbose | PipelineResultTypes.Debug | PipelineResultTypes.Information, - toResult: PipelineResultTypes.Output); - ps.Commands.AddCommand(cmd); - foreach (var arg in args) - { - ps.Commands.AddArgument(arg); - } - - try - { - // Invoke the script. - var results = ps.Invoke(); - - // Extract expected output types from results pipeline. - foreach (var psItem in results) - { - if (psItem == null || psItem.BaseObject == null) { continue; } - - switch (psItem.BaseObject) - { - case ErrorRecord error: - cmdlet.WriteError(error); - break; - - case WarningRecord warning: - cmdlet.WriteWarning(warning.Message); - break; - - case VerboseRecord verbose: - cmdlet.WriteVerbose(verbose.Message); - break; - - case DebugRecord debug: - cmdlet.WriteDebug(debug.Message); - break; - - case InformationRecord info: - cmdlet.WriteInformation(info); - break; - - case T result: - returnCollection.Add(result); - break; - - case T[] resultArray: - foreach (var item in resultArray) - { - returnCollection.Add(item); - } - break; - } - } - } - catch (Exception ex) - { - terminatingError = ex; - } - } - - return returnCollection; - } - - #endregion Methods - } - - #endregion PowerShellInvoker -} diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index 7a7e38f1c..cbf55ff9e 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -243,12 +243,10 @@ private IEnumerable SearchFromRepository( } else if (repositoryCredentialInfo != null) { - var authHelper = new CredentialInfoHelper(_cmdletPassedIn); - // TODO: Change this to return PSCredential - string password = authHelper.GetRepositoryCredentialInfoPassword( + string password = Utils.GetRepositoryCredentialFromSecretManagement( repositoryName, - repositoryCredentialInfo.VaultName, - repositoryCredentialInfo.SecretName); + repositoryCredentialInfo, + _cmdletPassedIn); source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repositoryCredentialInfo.SecretName, password, true, null); _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repositoryName); diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index da263cd51..e3ea3167e 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -283,7 +283,7 @@ private List InstallPackage( IEnumerable pkgsToInstall, string repoName, string repoUrl, - PSCredentialInfo repositoryCredentialInfo, + PSCredentialInfo repoCredentialInfo, PSCredential credential, bool isLocalRepo) { @@ -374,16 +374,14 @@ private List InstallPackage( string password = new NetworkCredential(string.Empty, credential.Password).Password; source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, credential.UserName, password, true, null); } - else if (repositoryCredentialInfo != null) + else if (repoCredentialInfo != null) { - var authHelper = new CredentialInfoHelper(_cmdletPassedIn); - // TODO: Change this to return PSCredential - string password = authHelper.GetRepositoryCredentialInfoPassword( + string password = Utils.GetRepositoryCredentialFromSecretManagement( repoName, - repositoryCredentialInfo.VaultName, - repositoryCredentialInfo.SecretName); + repoCredentialInfo, + _cmdletPassedIn); - source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repositoryCredentialInfo.SecretName, password, true, null); + source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repoCredentialInfo.SecretName, password, true, null); } var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider); SourceRepository repository = new SourceRepository(source, provider); diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index 6f73d0365..a4ab9925a 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -214,9 +214,30 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl throw new ArgumentException("Invalid url, must be one of the following Uri schemes: HTTPS, HTTP, FTP, File Based"); } - if (repoCredentialInfo?.Credential != null) + if(repoCredentialInfo != null) { - // TODO: Try adding credential to vault, throw terminating error if vault is inaccessible, etc. + bool isSecretManagementModuleAvailable = Utils.IsSecretManagementModuleAvailable(repoName, this); + + if (repoCredentialInfo.Credential != null) + { + if(!isSecretManagementModuleAvailable) + { + ThrowTerminatingError(new ErrorRecord( + new PSInvalidOperationException($"Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository {repoName}'s Credential in a vault."), + "RepositoryCredentialSecretManagementUnavailableModule", + ErrorCategory.ResourceUnavailable, + this)); + } + else + { + Utils.SaveRepositoryCredentialToSecretManagementVault(repoName, repoCredentialInfo, this); + } + } + + if(!isSecretManagementModuleAvailable) + { + WriteWarning($"Microsoft.PowerShell.SecretManagement module cannot be imported. Make sure it is available before performing PSResource operations in order to successfully authenticate to PSResourceRepository \"{repoName}\" with its CredentialInfo."); + } } WriteVerbose("All required values to add to repository provided, calling internal Add() API now"); diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs index 84b601a2d..6cbd1f46c 100644 --- a/src/code/SetPSResourceRepository.cs +++ b/src/code/SetPSResourceRepository.cs @@ -192,9 +192,30 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr // determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable) bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?(); - if (repoCredentialInfo?.Credential != null) + if(repoCredentialInfo != null) { - // TODO: Try adding credential to vault, throw terminating error if vault is inaccessible, etc. + bool isSecretManagementModuleAvailable = Utils.IsSecretManagementModuleAvailable(repoName, this); + + if (repoCredentialInfo.Credential != null) + { + if(!isSecretManagementModuleAvailable) + { + WriteError(new ErrorRecord( + new PSInvalidOperationException($"Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository {repoName}'s Credential in a vault."), + "RepositoryCredentialSecretManagementUnavailableModule", + ErrorCategory.ResourceUnavailable, + this)); + } + else + { + Utils.SaveRepositoryCredentialToSecretManagementVault(repoName, repoCredentialInfo, this); + } + } + + if(!isSecretManagementModuleAvailable) + { + WriteWarning($"Make sure the Microsoft.PowerShell.SecretManagement module is set up for successfully authenticating to PSResourceRepository \"{repoName}\" with its CredentialInfo."); + } } // determine if either 1 of 4 values are attempting to be set: URL, Priority, Trusted, CredentialInfo. diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 7b80bf999..69b093f67 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -5,14 +5,20 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.IO; using System.Linq; +using System.Net; using System.Management.Automation; using System.Management.Automation.Language; +using System.Management.Automation.Runspaces; using System.Runtime.InteropServices; +using System.Security; namespace Microsoft.PowerShell.PowerShellGet.UtilClasses { + #region Utils + internal static class Utils { #region String fields @@ -332,6 +338,193 @@ public static bool TryCreateValidPSCredentialInfo( } } + public static string GetRepositoryCredentialFromSecretManagement( + string repositoryName, + PSCredentialInfo repositoryCredentialInfo, + PSCmdlet cmdletPassedIn) + { + if(!IsSecretManagementVaultAccessible(repositoryName, repositoryCredentialInfo, cmdletPassedIn)) + { + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSInvalidOperationException($"Cannot access Microsoft.PowerShell.SecretManagement vault \"{repositoryCredentialInfo.VaultName}\" for PSResourceRepository ({repositoryName}) authentication."), + "RepositoryCredentialSecretManagementInaccessibleVault", + ErrorCategory.ResourceUnavailable, + cmdletPassedIn)); + return null; + } + + var results = PowerShellInvoker.InvokeScriptWithHost( + cmdlet: cmdletPassedIn, + script: @" + param ( + [string] $VaultName, + [string] $SecretName + ) + $module = Microsoft.PowerShell.Core\Import-Module -Name Microsoft.PowerShell.SecretManagement -PassThru + if ($null -eq $module) { + return + } + & $module ""Get-Secret"" -Name $SecretName -Vault $VaultName + ", + args: new object[] { repositoryCredentialInfo.VaultName, repositoryCredentialInfo.SecretName }, + out Exception terminatingError); + + var secretValue = (results.Count == 1) ? results[0] : null; + if (secretValue == null) + { + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSInvalidOperationException( + message: $"Microsoft.PowerShell.SecretManagement\\Get-Secret encountered an error while reading secret \"{repositoryCredentialInfo.SecretName}\" from vault \"{repositoryCredentialInfo.VaultName}\" for PSResourceRepository ({repositoryName}) authentication.", + innerException: terminatingError), + "RepositoryCredentialCannotGetSecretFromVault", + ErrorCategory.InvalidOperation, + cmdletPassedIn)); + } + + string passwordInPlainText = null; + switch(secretValue.GetType().Name) + { + case "String": + passwordInPlainText = (string) secretValue; + break; + case "SecureString": + passwordInPlainText = new NetworkCredential(string.Empty, (SecureString) secretValue).Password; + break; + case "PSCredential": + passwordInPlainText = new NetworkCredential(string.Empty, ((PSCredential) secretValue).Password).Password; + break; + default: + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSNotSupportedException($"Secret \"{repositoryCredentialInfo.SecretName}\" from vault \"{repositoryCredentialInfo.VaultName}\" has an invalid type. Types supported are String, SecureString, and PSCredential."), + "RepositoryCredentialInvalidSecretType", + ErrorCategory.InvalidType, + cmdletPassedIn)); + break; + } + + return passwordInPlainText; + } + + public static void SaveRepositoryCredentialToSecretManagementVault( + string repositoryName, + PSCredentialInfo repositoryCredentialInfo, + PSCmdlet cmdletPassedIn) + { + if(!IsSecretManagementVaultAccessible(repositoryName, repositoryCredentialInfo, cmdletPassedIn)) + { + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSInvalidOperationException($"Cannot access Microsoft.PowerShell.SecretManagement vault \"{repositoryCredentialInfo.VaultName}\" for PSResourceRepository ({repositoryName}) authentication."), + "RepositoryCredentialSecretManagementInaccessibleVault", + ErrorCategory.ResourceUnavailable, + cmdletPassedIn)); + return; + } + + PowerShellInvoker.InvokeScriptWithHost( + cmdlet: cmdletPassedIn, + script: @" + param ( + [string] $VaultName, + [string] $SecretName, + [object] $SecretValue + ) + $module = Microsoft.PowerShell.Core\Import-Module -Name Microsoft.PowerShell.SecretManagement -PassThru + if ($null -eq $module) { + return + } + & $module ""Set-Secret"" -Name $SecretName -Vault $VaultName -Secret $SecretValue + ", + args: new object[] { repositoryCredentialInfo.VaultName, repositoryCredentialInfo.SecretName, repositoryCredentialInfo.Credential }, + out Exception terminatingError); + + if (terminatingError != null) + { + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSInvalidOperationException( + message: $"Microsoft.PowerShell.SecretManagement\\Set-Secret encountered an error while adding secret \"{repositoryCredentialInfo.SecretName}\" to vault \"{repositoryCredentialInfo.VaultName}\" for PSResourceRepository ({repositoryName}) authentication.", + innerException: terminatingError), + "RepositoryCredentialCannotAddSecretToVault", + ErrorCategory.InvalidOperation, + cmdletPassedIn)); + } + } + + public static bool IsSecretManagementModuleAvailable( + string repositoryName, + PSCmdlet cmdletPassedIn) + { + var results = PowerShellInvoker.InvokeScriptWithHost( + cmdlet: cmdletPassedIn, + script: @" + $module = Microsoft.PowerShell.Core\Get-Module -Name Microsoft.PowerShell.SecretManagement -ErrorAction Ignore + if ($null -eq $module) { + $module = Microsoft.PowerShell.Core\Import-Module -Name Microsoft.PowerShell.SecretManagement -PassThru -ErrorAction Ignore + } + if ($null -eq $module) { + return 1 + } + return 0 + ", + args: new object[] {}, + out Exception terminatingError); + + if (terminatingError != null) + { + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSInvalidOperationException( + message: $"Cannot validate Microsoft.PowerShell.SecretManagement module setup for PSResourceRepository ({repositoryName}) authentication.", + innerException: terminatingError), + "RepositoryCredentialSecretManagementInvalidModule", + ErrorCategory.InvalidOperation, + cmdletPassedIn)); + } + + int result = (results.Count > 0) ? results[0] : 1; + return result == 0; + } + + public static bool IsSecretManagementVaultAccessible( + string repositoryName, + PSCredentialInfo repositoryCredentialInfo, + PSCmdlet cmdletPassedIn) + { + var results = PowerShellInvoker.InvokeScriptWithHost( + cmdlet: cmdletPassedIn, + script: @" + param ( + [string] $VaultName + ) + $module = Microsoft.PowerShell.Core\Import-Module -Name Microsoft.PowerShell.SecretManagement -PassThru + if ($null -eq $module) { + return + } + & $module ""Test-SecretVault"" -Name $VaultName + ", + args: new object[] { repositoryCredentialInfo.VaultName }, + out Exception terminatingError); + + if (terminatingError != null) + { + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSInvalidOperationException( + message: $"Microsoft.PowerShell.SecretManagement\\Test-SecretVault encountered an error while validating the vault \"{repositoryCredentialInfo.VaultName}\" for PSResourceRepository ({repositoryName}) authentication.", + innerException: terminatingError), + "RepositoryCredentialSecretManagementInvalidVault", + ErrorCategory.InvalidOperation, + cmdletPassedIn)); + } + + bool result = (results.Count > 0) ? results[0] : false; + return result; + } + #endregion #region Path methods @@ -725,4 +918,160 @@ private static void RestoreDirContents( #endregion } + + #endregion + + #region PowerShellInvoker + + internal static class PowerShellInvoker + { + #region Members + + private static bool _isHostDefault = false; + private const string DefaultHost = "Default Host"; + + private static Runspace _runspace; + + #endregion Members + + #region Methods + + public static Collection InvokeScriptWithHost( + PSCmdlet cmdlet, + string script, + object[] args, + out Exception terminatingError) + { + return InvokeScriptWithHost( + cmdlet, + script, + args, + out terminatingError); + } + + public static Collection InvokeScriptWithHost( + PSCmdlet cmdlet, + string script, + object[] args, + out Exception terminatingError) + { + Collection returnCollection = new Collection(); + terminatingError = null; + + // Create the runspace if it + // doesn't exist + // is not in a workable state + // has a default host (no UI) when a non-default host is available + if (_runspace == null || + _runspace.RunspaceStateInfo.State != RunspaceState.Opened || + _isHostDefault && !cmdlet.Host.Name.Equals(DefaultHost, StringComparison.InvariantCultureIgnoreCase)) + { + if (_runspace != null) + { + _runspace.Dispose(); + } + + _isHostDefault = cmdlet.Host.Name.Equals(DefaultHost, StringComparison.InvariantCultureIgnoreCase); + + var iss = InitialSessionState.CreateDefault2(); + // We are running trusted script. + iss.LanguageMode = PSLanguageMode.FullLanguage; + // Import the current PowerShellGet module. + var modPathObjects = cmdlet.InvokeCommand.InvokeScript( + script: "(Get-Module -Name PowerShellGet).Path"); + string modPath = (modPathObjects.Count > 0 && + modPathObjects[0].BaseObject is string modPathStr) + ? modPathStr : string.Empty; + if (!string.IsNullOrEmpty(modPath)) + { + iss.ImportPSModule(new string[] { modPath }); + } + + try + { + _runspace = RunspaceFactory.CreateRunspace(cmdlet.Host, iss); + _runspace.Open(); + } + catch (Exception ex) + { + terminatingError = ex; + return returnCollection; + } + } + + using (var ps = System.Management.Automation.PowerShell.Create()) + { + ps.Runspace = _runspace; + + var cmd = new Command( + command: script, + isScript: true, + useLocalScope: true); + cmd.MergeMyResults( + myResult: PipelineResultTypes.Error | PipelineResultTypes.Warning | PipelineResultTypes.Verbose | PipelineResultTypes.Debug | PipelineResultTypes.Information, + toResult: PipelineResultTypes.Output); + ps.Commands.AddCommand(cmd); + foreach (var arg in args) + { + ps.Commands.AddArgument(arg); + } + + try + { + // Invoke the script. + var results = ps.Invoke(); + + // Extract expected output types from results pipeline. + foreach (var psItem in results) + { + if (psItem == null || psItem.BaseObject == null) { continue; } + + switch (psItem.BaseObject) + { + case ErrorRecord error: + cmdlet.WriteError(error); + break; + + case WarningRecord warning: + cmdlet.WriteWarning(warning.Message); + break; + + case VerboseRecord verbose: + cmdlet.WriteVerbose(verbose.Message); + break; + + case DebugRecord debug: + cmdlet.WriteDebug(debug.Message); + break; + + case InformationRecord info: + cmdlet.WriteInformation(info); + break; + + case T result: + returnCollection.Add(result); + break; + + case T[] resultArray: + foreach (var item in resultArray) + { + returnCollection.Add(item); + } + break; + } + } + } + catch (Exception ex) + { + terminatingError = ex; + } + } + + return returnCollection; + } + + #endregion Methods + } + + #endregion } diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index 1888c6262..b4dc71b0b 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -18,9 +18,6 @@ Describe "Test Register-PSResourceRepository" { $relativeCurrentPath = Get-Location $credentialInfo1 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret") - $secureString = ConvertTo-SecureString "testpassword" -AsPlainText -Force - $credential = New-Object System.Management.Automation.PSCredential ("testusername", $secureString) - $credentialInfo2 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $credential) } AfterEach { Get-RevertPSResourceRepositoryFile @@ -57,15 +54,13 @@ Describe "Test Register-PSResourceRepository" { } It "register repository given Name, URL, Trusted, Priority, CredentialInfo (NameParameterSet)" { - $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo2 -PassThru + $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo1 -PassThru $res.Name | Should -Be "testRepository" $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Trusted | Should -Be True $res.Priority | Should -Be 20 $res.CredentialInfo.VaultName | Should -Be "testvault" $res.CredentialInfo.SecretName | Should -Be "testsecret" - $res.CredentialInfo.Credential | Should -Not -BeNullOrEmpty - $res.CredentialInfo.Credential.UserName | Should -Be "testusername" } It "register repository with PSGallery parameter (PSGalleryParameterSet)" { @@ -143,7 +138,7 @@ Describe "Test Register-PSResourceRepository" { $hashtable2 = @{Name = "testRepository"; URL = $tmpDir1Path} $hashtable3 = @{Name = "testRepository2"; URL = $tmpDir2Path; Trusted = $True} $hashtable4 = @{Name = "testRepository3"; URL = $tmpDir3Path; Trusted = $True; Priority = 20} - $hashtable5 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = (New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", (New-Object System.Management.Automation.PSCredential ("testusername", (ConvertTo-SecureString "testpassword" -AsPlainText -Force)))))} + $hashtable5 = @{Name = "testRepository4"; URL = $tmpDir4Path; Trusted = $True; Priority = 30; CredentialInfo = (New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret"))} $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4, $hashtable5 Register-PSResourceRepository -Repositories $arrayOfHashtables @@ -174,8 +169,6 @@ Describe "Test Register-PSResourceRepository" { $res5.Priority | Should -Be 30 $res5.CredentialInfo.VaultName | Should -Be "testvault" $res5.CredentialInfo.SecretName | Should -Be "testsecret" - # Get-PSResourceRepository does not return Credential information as it is not saved - # TODO: update based on how username vs secretname is saved $res5.CredentialInfo.Credential | Should -BeNullOrEmpty } diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 9f4d852e7..578deb17c 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -17,9 +17,7 @@ Describe "Test Set-PSResourceRepository" { $relativeCurrentPath = Get-Location - $secureString = ConvertTo-SecureString "testpassword" -AsPlainText -Force - $credential = New-Object System.Management.Automation.PSCredential ("testusername", $secureString) - $credentialInfo1 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $credential) + $credentialInfo1 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret") } AfterEach { Get-RevertPSResourceRepositoryFile @@ -74,8 +72,6 @@ Describe "Test Set-PSResourceRepository" { $res.Trusted | Should -Be False $res.CredentialInfo.VaultName | Should -Be "testvault" $res.CredentialInfo.SecretName | Should -Be "testsecret" - # Get-PSResourceRepository does not return Credential information as it is not saved - # TODO: update based on how username vs secretname is saved $res.CredentialInfo.Credential | Should -BeNullOrEmpty } @@ -131,7 +127,7 @@ Describe "Test Set-PSResourceRepository" { $hashtable1 = @{Name = "testRepository1"; URL = $tmpDir2Path}; $hashtable2 = @{Name = "testRepository2"; Priority = 25}; - $hashtable3 = @{Name = "testRepository3"; CredentialInfo = [PSCustomObject] @{ VaultName = "testvault"; SecretName = "testsecret"; Credential = [PSCredential] $credential }}; + $hashtable3 = @{Name = "testRepository3"; CredentialInfo = [PSCustomObject] @{ VaultName = "testvault"; SecretName = "testsecret" }}; $hashtable4 = @{Name = "PSGallery"; Trusted = $True}; $arrayOfHashtables = $hashtable1, $hashtable2, $hashtable3, $hashtable4 @@ -157,8 +153,6 @@ Describe "Test Set-PSResourceRepository" { $res3.Trusted | Should -Be False $res3.CredentialInfo.VaultName | Should -Be "testvault" $res3.CredentialInfo.SecretName | Should -Be "testsecret" - # Get-PSResourceRepository does not return Credential information as it is not saved - # TODO: update based on how username vs secretname is saved $res3.CredentialInfo.Credential | Should -BeNullOrEmpty $res4 = Get-PSResourceRepository -Name $PSGalleryName From dcbbb5cda7030c70435c72e6b2786b77aa5c5981 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Wed, 5 Jan 2022 17:54:35 -0600 Subject: [PATCH 12/20] add PSRepositoryInfo to PSGet.Format.ps1xml to not display CredentialInfo --- src/PSGet.Format.ps1xml | 98 +++++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 37 deletions(-) diff --git a/src/PSGet.Format.ps1xml b/src/PSGet.Format.ps1xml index 7b47f3878..8baea53f1 100644 --- a/src/PSGet.Format.ps1xml +++ b/src/PSGet.Format.ps1xml @@ -1,19 +1,19 @@ - + PSResourceInfo Microsoft.PowerShell.PowerShellGet.UtilClasses.PSResourceInfo - + - - - - - - - + + + + + + + @@ -23,42 +23,66 @@ Repository Description - + PSIncludedResourceInfoTable - Microsoft.PowerShell.PowerShellGet.UtilClasses.PSIncludedResourceInfo + Microsoft.PowerShell.PowerShellGet.UtilClasses.PSIncludedResourceInfo - - - - - - - - - - - - - - - - - - - Name - $_.ParentResource.Version - $_.ParentResource.PrereleaseLabel - $_.ParentResource.Name - $_.ParentResource.Repository - - - + + + + + + + + + + + + + + + + + + + Name + $_.ParentResource.Version + $_.ParentResource.PrereleaseLabel + $_.ParentResource.Name + $_.ParentResource.Repository + + + + + + + PSRepositoryInfo + + Microsoft.PowerShell.PowerShellGet.UtilClasses.PSRepositoryInfo + + + + + + + + + + + + Name + Url + Trusted + Priority + + + From 27d8a75a1eb0518b8c8c8d4307ba2adff8a77fa7 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Wed, 5 Jan 2022 18:17:38 -0600 Subject: [PATCH 13/20] address outstanding merge conflicts --- src/code/InstallHelper.cs | 2 ++ src/code/RepositorySettings.cs | 8 +++++++- test/SetPSResourceRepository.Tests.ps1 | 5 +++-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index ffd7a9718..2eb21ed25 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -226,6 +226,7 @@ private List ProcessRepositories( List pkgsInstalled = InstallPackage( pkgsFromRepoToInstall, + repoName, repo.Url.AbsoluteUri, repo.CredentialInfo, credential, @@ -304,6 +305,7 @@ private IEnumerable FilterByInstalledPkgs(IEnumerable InstallPackage( IEnumerable pkgsToInstall, // those found to be required to be installed (includes Dependency packages as well) + string repoName, string repoUrl, PSCredentialInfo repoCredentialInfo, PSCredential credential, diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs index a70ed767e..1ef87646d 100644 --- a/src/code/RepositorySettings.cs +++ b/src/code/RepositorySettings.cs @@ -248,11 +248,17 @@ public static List Remove(string[] repoNames, out string[] err continue; } + PSCredentialInfo repoCredentialInfo = null; + if(node.Attribute("VaultName") != null & node.Attribute("SecretName") != null) + { + repoCredentialInfo = new PSCredentialInfo(node.Attribute("VaultName").Value, node.Attribute("SecretName").Value); + } removedRepos.Add( new PSRepositoryInfo(repo, new Uri(node.Attribute("Url").Value), Int32.Parse(node.Attribute("Priority").Value), - Boolean.Parse(node.Attribute("Trusted").Value))); + Boolean.Parse(node.Attribute("Trusted").Value), + repoCredentialInfo)); // Remove item from file node.Remove(); } diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 70ad1b90b..b86955e9b 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -9,6 +9,7 @@ Describe "Test Set-PSResourceRepository" { $PSGalleryURL = Get-PSGalleryLocation $TestRepoName1 = "testRepository" $TestRepoName2 = "testRepository2" + $TestRepoName3 = "testRepository3" $relativeCurrentPath = Get-Location Get-NewPSResourceRepositoryFile $tmpDir1Path = Join-Path -Path $TestDrive -ChildPath "tmpDir1" @@ -149,8 +150,8 @@ Describe "Test Set-PSResourceRepository" { $res2.Trusted | Should -Be False $res2.CredentialInfo | Should -BeNullOrEmpty - $res3 = Get-PSResourceRepository -Name "testRepository3" - $res3.Name | Should -Be "testRepository3" + $res3 = Get-PSResourceRepository -Name $TestRepoName3 + $res3.Name | Should -Be $TestRepoName3 $res3.URL.LocalPath | Should -Contain $tmpDir3Path $res3.Priority | Should -Be 50 $res3.Trusted | Should -Be False From a955effbaa4ea35b86602f881af8e973d269fea2 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Wed, 5 Jan 2022 20:34:26 -0600 Subject: [PATCH 14/20] change valid type to only PSCredential --- src/code/FindHelper.cs | 5 +++-- src/code/InstallHelper.cs | 5 +++-- src/code/Utils.cs | 32 +++++++++++--------------------- 3 files changed, 17 insertions(+), 25 deletions(-) diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index cbf55ff9e..7cdbe68c5 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -243,12 +243,13 @@ private IEnumerable SearchFromRepository( } else if (repositoryCredentialInfo != null) { - string password = Utils.GetRepositoryCredentialFromSecretManagement( + PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement( repositoryName, repositoryCredentialInfo, _cmdletPassedIn); - source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repositoryCredentialInfo.SecretName, password, true, null); + string password = new NetworkCredential(string.Empty, repoCredential.Password).Password; + source.Credentials = PackageSourceCredential.FromUserInput(repositoryUrl.ToString(), repoCredential.UserName, password, true, null); _cmdletPassedIn.WriteVerbose("credential successfully read from vault and set for repository: " + repositoryName); } diff --git a/src/code/InstallHelper.cs b/src/code/InstallHelper.cs index 2eb21ed25..5094b1f02 100644 --- a/src/code/InstallHelper.cs +++ b/src/code/InstallHelper.cs @@ -411,12 +411,13 @@ private List InstallPackage( } else if (repoCredentialInfo != null) { - string password = Utils.GetRepositoryCredentialFromSecretManagement( + PSCredential repoCredential = Utils.GetRepositoryCredentialFromSecretManagement( repoName, repoCredentialInfo, _cmdletPassedIn); - source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repoCredentialInfo.SecretName, password, true, null); + string password = new NetworkCredential(string.Empty, repoCredential.Password).Password; + source.Credentials = PackageSourceCredential.FromUserInput(repoUrl, repoCredential.UserName, password, true, null); } var provider = FactoryExtensionsV3.GetCoreV3(NuGet.Protocol.Core.Types.Repository.Provider); SourceRepository repository = new SourceRepository(source, provider); diff --git a/src/code/Utils.cs b/src/code/Utils.cs index b40633357..d35e4ae69 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -338,7 +338,7 @@ public static bool TryCreateValidPSCredentialInfo( } } - public static string GetRepositoryCredentialFromSecretManagement( + public static PSCredential GetRepositoryCredentialFromSecretManagement( string repositoryName, PSCredentialInfo repositoryCredentialInfo, PSCmdlet cmdletPassedIn) @@ -383,29 +383,19 @@ public static string GetRepositoryCredentialFromSecretManagement( cmdletPassedIn)); } - string passwordInPlainText = null; - switch(secretValue.GetType().Name) + if(secretValue is PSCredential) { - case "String": - passwordInPlainText = (string) secretValue; - break; - case "SecureString": - passwordInPlainText = new NetworkCredential(string.Empty, (SecureString) secretValue).Password; - break; - case "PSCredential": - passwordInPlainText = new NetworkCredential(string.Empty, ((PSCredential) secretValue).Password).Password; - break; - default: - cmdletPassedIn.ThrowTerminatingError( - new ErrorRecord( - new PSNotSupportedException($"Secret \"{repositoryCredentialInfo.SecretName}\" from vault \"{repositoryCredentialInfo.VaultName}\" has an invalid type. Types supported are String, SecureString, and PSCredential."), - "RepositoryCredentialInvalidSecretType", - ErrorCategory.InvalidType, - cmdletPassedIn)); - break; + return (PSCredential) secretValue; } - return passwordInPlainText; + cmdletPassedIn.ThrowTerminatingError( + new ErrorRecord( + new PSNotSupportedException($"Secret \"{repositoryCredentialInfo.SecretName}\" from vault \"{repositoryCredentialInfo.VaultName}\" has an invalid type. The only supported type is PSCredential."), + "RepositoryCredentialInvalidSecretType", + ErrorCategory.InvalidType, + cmdletPassedIn)); + + return null; } public static void SaveRepositoryCredentialToSecretManagementVault( From 1d2876742f4f4daab58dc864c7e44d7dc2fd0316 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Wed, 5 Jan 2022 21:36:16 -0600 Subject: [PATCH 15/20] fix error id after parameter rename --- src/code/RegisterPSResourceRepository.cs | 2 +- test/RegisterPSResourceRepository.Tests.ps1 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index a4ab9925a..e7b408199 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -278,7 +278,7 @@ private List RepositoriesParameterSetHelper() { WriteError(new ErrorRecord( new PSInvalidOperationException("Repository hashtable cannot contain PSGallery key with -Name, -URL and/or -CredentialInfo key value pairs"), - "NotProvideNameUrlAuthForPSGalleryRepositoriesParameterSetRegistration", + "NotProvideNameUrlCredentialInfoForPSGalleryRepositoriesParameterSetRegistration", ErrorCategory.InvalidArgument, this)); continue; diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index 7eaef5610..d81f99bd3 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -219,7 +219,7 @@ Describe "Test Register-PSResourceRepository" { Unregister-PSResourceRepository -Name $PSGalleryName Register-PSResourceRepository -Repositories $arrayOfHashtables -ErrorVariable err -ErrorAction SilentlyContinue $err.Count | Should -Not -Be 0 - $err[0].FullyQualifiedErrorId | Should -BeExactly "NotProvideNameUrlAuthForPSGalleryRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + $err[0].FullyQualifiedErrorId | Should -BeExactly "NotProvideNameUrlCredentialInfoForPSGalleryRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" $res = Get-PSResourceRepository -Name $TestRepoName1 $res.Name | Should -Be $TestRepoName1 From c3d81dd5b572f098943ba528c95d573ad9bd3d83 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Thu, 6 Jan 2022 18:26:48 -0600 Subject: [PATCH 16/20] add tests for SecretManagement setup validation, fix error and warning message in SetPSResourceRepository --- src/code/SetPSResourceRepository.cs | 4 +-- test/RegisterPSResourceRepository.Tests.ps1 | 30 ++++++++++++++---- test/SetPSResourceRepository.Tests.ps1 | 34 +++++++++++++++++---- 3 files changed, 54 insertions(+), 14 deletions(-) diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs index 6cbd1f46c..82fe95cd1 100644 --- a/src/code/SetPSResourceRepository.cs +++ b/src/code/SetPSResourceRepository.cs @@ -200,7 +200,7 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr { if(!isSecretManagementModuleAvailable) { - WriteError(new ErrorRecord( + ThrowTerminatingError(new ErrorRecord( new PSInvalidOperationException($"Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository {repoName}'s Credential in a vault."), "RepositoryCredentialSecretManagementUnavailableModule", ErrorCategory.ResourceUnavailable, @@ -214,7 +214,7 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr if(!isSecretManagementModuleAvailable) { - WriteWarning($"Make sure the Microsoft.PowerShell.SecretManagement module is set up for successfully authenticating to PSResourceRepository \"{repoName}\" with its CredentialInfo."); + WriteWarning($"Microsoft.PowerShell.SecretManagement module cannot be imported. Make sure it is available before performing PSResource operations in order to successfully authenticate to PSResourceRepository \"{repoName}\" with its CredentialInfo."); } } diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index d81f99bd3..9e6ebb5bf 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -23,6 +23,9 @@ Describe "Test Register-PSResourceRepository" { $relativeCurrentPath = Get-Location $credentialInfo1 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret") + $secureString = ConvertTo-SecureString "testpassword" -AsPlainText -Force + $credential = New-Object pscredential ("testusername", $secureString) + $credentialInfo2 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $credential) } AfterEach { Get-RevertPSResourceRepositoryFile @@ -59,8 +62,8 @@ Describe "Test Register-PSResourceRepository" { } It "register repository given Name, URL, Trusted, Priority, CredentialInfo (NameParameterSet)" { - $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo1 -PassThru - $res.Name | Should -Be "testRepository" + $res = Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo1 -PassThru + $res.Name | Should -Be $TestRepoName1 $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Trusted | Should -Be True $res.Priority | Should -Be 20 @@ -231,10 +234,10 @@ Describe "Test Register-PSResourceRepository" { $res3.Name | Should -Be $TestRepoName3 } - $testCases2 = @{Type = "-Name is not specified"; IncorrectHashTable = @{URL = $tmpDir1Path}; ErrorId = "NullNameForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-Name is PSGallery"; IncorrectHashTable = @{Name = "PSGallery"; URL = $tmpDir1Path}; ErrorId = "PSGalleryProvidedAsNameRepoPSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-URL not specified"; IncorrectHashTable = @{Name = "testRepository"}; ErrorId = "NullURLForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, - @{Type = "-URL is not valid scheme"; IncorrectHashTable = @{Name = "testRepository"; URL="www.google.com"}; ErrorId = "InvalidUri,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} + $testCases2 = @{Type = "-Name is not specified"; IncorrectHashTable = @{URL = $tmpDir1Path}; ErrorId = "NullNameForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-Name is PSGallery"; IncorrectHashTable = @{Name = $PSGalleryName; URL = $tmpDir1Path}; ErrorId = "PSGalleryProvidedAsNameRepoPSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-URL not specified"; IncorrectHashTable = @{Name = $TestRepoName1}; ErrorId = "NullURLForRepositoriesParameterSetRegistration,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"}, + @{Type = "-URL is not valid scheme"; IncorrectHashTable = @{Name = $TestRepoName1; URL="www.google.com"}; ErrorId = "InvalidUri,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository"} It "not register incorrectly formatted Name type repo among correct ones when incorrect type is " -TestCases $testCases2 { param($Type, $IncorrectHashTable, $ErrorId) @@ -277,4 +280,19 @@ Describe "Test Register-PSResourceRepository" { $res.Name | Should -Be "localFileShareTestRepo" $res.URL.LocalPath | Should -Contain "\\hcgg.rest.of.domain.name\test\ITxx\team\NuGet\" } + + It "prints a warning if CredentialInfo is passed in without SecretManagement module setup" { + $output = Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo1 3>&1 + $output | Should -Match "Microsoft.PowerShell.SecretManagement module cannot be imported" + + $res = Get-PSResourceRepository -Name $TestRepoName1 + $res | Should -Not -BeNullOrEmpty + } + + It "throws error if CredentialInfo is passed in with Credential property without SecretManagement module setup" { + { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo2 } | Should -Throw "Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" + + $res = Get-PSResourceRepository -Name $TestRepoName1 -ErrorAction Ignore + $res | Should -BeNullOrEmpty + } } diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index b86955e9b..3d20a99b1 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -22,6 +22,9 @@ Describe "Test Set-PSResourceRepository" { $relativeCurrentPath = Get-Location $credentialInfo1 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret") + $secureString = ConvertTo-SecureString "testpassword" -AsPlainText -Force + $credential = New-Object pscredential ("testusername", $secureString) + $credentialInfo2 = New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "testsecret", $credential) } AfterEach { Get-RevertPSResourceRepositoryFile @@ -67,10 +70,10 @@ Describe "Test Set-PSResourceRepository" { } It "set repository given Name and CredentialInfo parameters" { - Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path - Set-PSResourceRepository -Name "testRepository" -CredentialInfo $credentialInfo1 - $res = Get-PSResourceRepository -Name "testRepository" - $res.Name | Should -Be "testRepository" + Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path + Set-PSResourceRepository -Name $TestRepoName1 -CredentialInfo $credentialInfo1 + $res = Get-PSResourceRepository -Name $TestRepoName1 + $res.Name | Should -Be $TestRepoName1 $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False @@ -174,9 +177,9 @@ Describe "Test Set-PSResourceRepository" { } It "not set and throw error for trying to set PSGallery CredentialInfo (NameParameterSet)" { - Unregister-PSResourceRepository -Name "PSGallery" + Unregister-PSResourceRepository -Name $PSGalleryName Register-PSResourceRepository -PSGallery - {Set-PSResourceRepository -Name "PSGallery" -CredentialInfo $credentialInfo1 -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" + {Set-PSResourceRepository -Name $PSGalleryName -CredentialInfo $credentialInfo1 -ErrorAction Stop} | Should -Throw -ErrorId "ErrorInNameParameterSet,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" } It "not set repository and throw error for trying to set PSGallery URL (RepositoriesParameterSet)" { @@ -246,4 +249,23 @@ Describe "Test Set-PSResourceRepository" { $res.Priority | Should -Be 50 $res.Trusted | Should -Be False } + + It "prints a warning if CredentialInfo is passed in without SecretManagement module setup" { + Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path + $output = Set-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -CredentialInfo $credentialInfo1 3>&1 + $output | Should -Match "Microsoft.PowerShell.SecretManagement module cannot be imported" + + $res = Get-PSResourceRepository -Name $TestRepoName1 + $res | Should -Not -BeNullOrEmpty + } + + It "throws error if CredentialInfo is passed in with Credential property without SecretManagement module setup" { + { + Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path + Set-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -CredentialInfo $credentialInfo2 + } | Should -Throw "Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" + + $res = Get-PSResourceRepository -Name $TestRepoName1 -ErrorAction Ignore + $res.CredentialInfo | Should -BeNullOrEmpty + } } From f7e7a1fcab7e0b5072bca7660d44f54876b826f0 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Wed, 12 Jan 2022 20:20:37 -0600 Subject: [PATCH 17/20] make style adjustments --- src/code/FindHelper.cs | 4 +- src/code/PSCredentialInfo.cs | 8 ++-- src/code/RegisterPSResourceRepository.cs | 23 ++++++----- src/code/RepositorySettings.cs | 43 +++++++++++++-------- src/code/SetPSResourceRepository.cs | 25 ++++++------ src/code/Utils.cs | 12 +++--- test/RegisterPSResourceRepository.Tests.ps1 | 4 +- test/SetPSResourceRepository.Tests.ps1 | 4 +- 8 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/code/FindHelper.cs b/src/code/FindHelper.cs index 7cdbe68c5..e7a3d7726 100644 --- a/src/code/FindHelper.cs +++ b/src/code/FindHelper.cs @@ -141,7 +141,7 @@ public IEnumerable FindByResourceName( // detect if Script repository needs to be added and/or Module repository needs to be skipped Uri psGalleryScriptsUrl = new Uri("http://www.powershellgallery.com/api/v2/items/psscript/"); - PSRepositoryInfo psGalleryScripts = new PSRepositoryInfo(_psGalleryScriptsRepoName, psGalleryScriptsUrl, repositoriesToSearch[i].Priority, false, null); + PSRepositoryInfo psGalleryScripts = new PSRepositoryInfo(_psGalleryScriptsRepoName, psGalleryScriptsUrl, repositoriesToSearch[i].Priority, trusted: false, credentialInfo: null); if (_type == ResourceType.None) { _cmdletPassedIn.WriteVerbose("Null Type provided, so add PSGalleryScripts repository"); @@ -161,7 +161,7 @@ public IEnumerable FindByResourceName( // detect if Script repository needs to be added and/or Module repository needs to be skipped Uri poshTestGalleryScriptsUrl = new Uri("https://www.poshtestgallery.com/api/v2/items/psscript/"); - PSRepositoryInfo poshTestGalleryScripts = new PSRepositoryInfo(_poshTestGalleryScriptsRepoName, poshTestGalleryScriptsUrl, repositoriesToSearch[i].Priority, false, null); + PSRepositoryInfo poshTestGalleryScripts = new PSRepositoryInfo(_poshTestGalleryScriptsRepoName, poshTestGalleryScriptsUrl, repositoriesToSearch[i].Priority, trusted: false, credentialInfo: null); if (_type == ResourceType.None) { _cmdletPassedIn.WriteVerbose("Null Type provided, so add PoshTestGalleryScripts repository"); diff --git a/src/code/PSCredentialInfo.cs b/src/code/PSCredentialInfo.cs index f9b8926d6..11ec0f198 100644 --- a/src/code/PSCredentialInfo.cs +++ b/src/code/PSCredentialInfo.cs @@ -36,7 +36,7 @@ public PSCredentialInfo(string vaultName, string secretName, PSCredential creden /// public PSCredentialInfo(PSObject psObject) { - if(psObject == null) + if (psObject == null) { throw new ArgumentNullException(nameof(psObject)); } @@ -59,9 +59,10 @@ public string VaultName { { return _vaultName; } + private set { - if(string.IsNullOrEmpty(value)) + if (string.IsNullOrEmpty(value)) { throw new ArgumentException($"Invalid CredentialInfo, {PSCredentialInfo.VaultNameAttribute} must be a non-empty string"); } @@ -79,9 +80,10 @@ public string SecretName { { return _secretName; } + private set { - if(string.IsNullOrEmpty(value)) + if (string.IsNullOrEmpty(value)) { throw new ArgumentException($"Invalid CredentialInfo, {PSCredentialInfo.SecretNameAttribute} must be a non-empty string"); } diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index e7b408199..4ac0e9861 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -214,16 +214,16 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl throw new ArgumentException("Invalid url, must be one of the following Uri schemes: HTTPS, HTTP, FTP, File Based"); } - if(repoCredentialInfo != null) + if (repoCredentialInfo != null) { bool isSecretManagementModuleAvailable = Utils.IsSecretManagementModuleAvailable(repoName, this); if (repoCredentialInfo.Credential != null) { - if(!isSecretManagementModuleAvailable) + if (!isSecretManagementModuleAvailable) { ThrowTerminatingError(new ErrorRecord( - new PSInvalidOperationException($"Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository {repoName}'s Credential in a vault."), + new PSInvalidOperationException($"Microsoft.PowerShell.SecretManagement module is not found, but is required for saving PSResourceRepository {repoName}'s Credential in a vault."), "RepositoryCredentialSecretManagementUnavailableModule", ErrorCategory.ResourceUnavailable, this)); @@ -234,9 +234,9 @@ private PSRepositoryInfo AddToRepositoryStoreHelper(string repoName, Uri repoUrl } } - if(!isSecretManagementModuleAvailable) + if (!isSecretManagementModuleAvailable) { - WriteWarning($"Microsoft.PowerShell.SecretManagement module cannot be imported. Make sure it is available before performing PSResource operations in order to successfully authenticate to PSResourceRepository \"{repoName}\" with its CredentialInfo."); + WriteWarning($"Microsoft.PowerShell.SecretManagement module cannot be found. Make sure it is installed before performing PSResource operations in order to successfully authenticate to PSResourceRepository \"{repoName}\" with its CredentialInfo."); } } @@ -264,7 +264,7 @@ private PSRepositoryInfo PSGalleryParameterSetHelper(int repoPriority, bool repo { Uri psGalleryUri = new Uri(PSGalleryRepoURL); WriteVerbose("(PSGallerySet) internal name and uri values for Add() API are hardcoded and validated, priority and trusted values, if passed in, also validated"); - return AddToRepositoryStoreHelper(PSGalleryRepoName, psGalleryUri, repoPriority, repoTrusted, null); + return AddToRepositoryStoreHelper(PSGalleryRepoName, psGalleryUri, repoPriority, repoTrusted, repoCredentialInfo: null); } private List RepositoriesParameterSetHelper() @@ -355,15 +355,14 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) } PSCredentialInfo repoCredentialInfo = null; - if(repo.ContainsKey("CredentialInfo")) { - if (!Utils.TryCreateValidPSCredentialInfo(credentialInfoCandidate: (PSObject) repo["CredentialInfo"], + if (repo.ContainsKey("CredentialInfo") && + !Utils.TryCreateValidPSCredentialInfo(credentialInfoCandidate: (PSObject) repo["CredentialInfo"], cmdletPassedIn: this, repoCredentialInfo: out repoCredentialInfo, errorRecord: out ErrorRecord errorRecord1)) - { - WriteError(errorRecord1); - return null; - } + { + WriteError(errorRecord1); + return null; } try diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs index 1ef87646d..745230376 100644 --- a/src/code/RepositorySettings.cs +++ b/src/code/RepositorySettings.cs @@ -60,7 +60,7 @@ public static void CheckRepositoryStore() // Add PSGallery to the newly created store Uri psGalleryUri = new Uri(PSGalleryRepoURL); - Add(PSGalleryRepoName, psGalleryUri, defaultPriority, defaultTrusted, null); + Add(PSGalleryRepoName, psGalleryUri, defaultPriority, defaultTrusted, repoCredentialInfo: null); } // Open file (which should exist now), if cannot/is corrupted then throw error @@ -103,7 +103,8 @@ public static PSRepositoryInfo Add(string repoName, Uri repoURL, int repoPriorit new XAttribute("Trusted", repoTrusted) ); - if(repoCredentialInfo != null) { + if (repoCredentialInfo != null) + { newElement.Add(new XAttribute(PSCredentialInfo.VaultNameAttribute, repoCredentialInfo.VaultName)); newElement.Add(new XAttribute(PSCredentialInfo.SecretNameAttribute, repoCredentialInfo.SecretName)); } @@ -164,20 +165,24 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio } // A null CredentialInfo value passed in signifies that CredentialInfo was not attempted to be set. - // So only set VaultName and SecretName attributes if non-null value passed in for repoCredentialInfo + // Set VaultName and SecretName attributes if non-null value passed in for repoCredentialInfo if (repoCredentialInfo != null) { - if (node.Attribute(PSCredentialInfo.VaultNameAttribute) == null) { + if (node.Attribute(PSCredentialInfo.VaultNameAttribute) == null) + { node.Add(new XAttribute(PSCredentialInfo.VaultNameAttribute, repoCredentialInfo.VaultName)); } - else { + else + { node.Attribute(PSCredentialInfo.VaultNameAttribute).Value = repoCredentialInfo.VaultName; } - if (node.Attribute(PSCredentialInfo.SecretNameAttribute) == null) { + if (node.Attribute(PSCredentialInfo.SecretNameAttribute) == null) + { node.Add(new XAttribute(PSCredentialInfo.SecretNameAttribute, repoCredentialInfo.SecretName)); } - else { + else + { node.Attribute(PSCredentialInfo.SecretNameAttribute).Value = repoCredentialInfo.SecretName; } } @@ -192,9 +197,9 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio PSCredentialInfo thisCredentialInfo; try { - thisCredentialInfo = new PSCredentialInfo(node.Attribute(PSCredentialInfo.VaultNameAttribute).Value, node.Attribute(PSCredentialInfo.SecretNameAttribute).Value); + thisCredentialInfo = new PSCredentialInfo(node.Attribute(PSCredentialInfo.VaultNameAttribute)?.Value, node.Attribute(PSCredentialInfo.SecretNameAttribute)?.Value); } - catch (Exception) + catch (ArgumentException) { thisCredentialInfo = null; } @@ -249,7 +254,7 @@ public static List Remove(string[] repoNames, out string[] err } PSCredentialInfo repoCredentialInfo = null; - if(node.Attribute("VaultName") != null & node.Attribute("SecretName") != null) + if (node.Attribute("VaultName") != null & node.Attribute("SecretName") != null) { repoCredentialInfo = new PSCredentialInfo(node.Attribute("VaultName").Value, node.Attribute("SecretName").Value); } @@ -301,7 +306,8 @@ public static List Read(string[] repoNames, out string[] error PSCredentialInfo thisCredentialInfo; string credentialInfoErrorMessage = $"Repository {repo.Attribute("Name").Value} has invalid CredentialInfo. {PSCredentialInfo.VaultNameAttribute} and {PSCredentialInfo.SecretNameAttribute} should both be present and non-empty"; // both keys are present - if (repo.Attribute(PSCredentialInfo.VaultNameAttribute) != null && repo.Attribute(PSCredentialInfo.SecretNameAttribute) != null) { + if (repo.Attribute(PSCredentialInfo.VaultNameAttribute) != null && repo.Attribute(PSCredentialInfo.SecretNameAttribute) != null) + { try { // both values are non-empty @@ -316,12 +322,14 @@ public static List Read(string[] repoNames, out string[] error } } // both keys are missing - else if (repo.Attribute(PSCredentialInfo.VaultNameAttribute) == null && repo.Attribute(PSCredentialInfo.SecretNameAttribute) == null) { + else if (repo.Attribute(PSCredentialInfo.VaultNameAttribute) == null && repo.Attribute(PSCredentialInfo.SecretNameAttribute) == null) + { // = valid credentialInfo thisCredentialInfo = null; } // one of the keys is missing - else { + else + { thisCredentialInfo = null; tempErrorList.Add(credentialInfoErrorMessage); continue; @@ -356,7 +364,8 @@ public static List Read(string[] repoNames, out string[] error PSCredentialInfo thisCredentialInfo; string credentialInfoErrorMessage = $"Repository {node.Attribute("Name").Value} has invalid CredentialInfo. {PSCredentialInfo.VaultNameAttribute} and {PSCredentialInfo.SecretNameAttribute} should both be present and non-empty"; // both keys are present - if (node.Attribute(PSCredentialInfo.VaultNameAttribute) != null && node.Attribute(PSCredentialInfo.SecretNameAttribute) != null) { + if (node.Attribute(PSCredentialInfo.VaultNameAttribute) != null && node.Attribute(PSCredentialInfo.SecretNameAttribute) != null) + { try { // both values are non-empty @@ -371,12 +380,14 @@ public static List Read(string[] repoNames, out string[] error } } // both keys are missing - else if (node.Attribute(PSCredentialInfo.VaultNameAttribute) == null && node.Attribute(PSCredentialInfo.SecretNameAttribute) == null) { + else if (node.Attribute(PSCredentialInfo.VaultNameAttribute) == null && node.Attribute(PSCredentialInfo.SecretNameAttribute) == null) + { // = valid credentialInfo thisCredentialInfo = null; } // one of the keys is missing - else { + else + { thisCredentialInfo = null; tempErrorList.Add(credentialInfoErrorMessage); continue; diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs index 82fe95cd1..2ee10e5ae 100644 --- a/src/code/SetPSResourceRepository.cs +++ b/src/code/SetPSResourceRepository.cs @@ -192,16 +192,16 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr // determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable) bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?(); - if(repoCredentialInfo != null) + if (repoCredentialInfo != null) { bool isSecretManagementModuleAvailable = Utils.IsSecretManagementModuleAvailable(repoName, this); if (repoCredentialInfo.Credential != null) { - if(!isSecretManagementModuleAvailable) + if (!isSecretManagementModuleAvailable) { ThrowTerminatingError(new ErrorRecord( - new PSInvalidOperationException($"Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository {repoName}'s Credential in a vault."), + new PSInvalidOperationException($"Microsoft.PowerShell.SecretManagement module is not found, but is required for saving PSResourceRepository {repoName}'s Credential in a vault."), "RepositoryCredentialSecretManagementUnavailableModule", ErrorCategory.ResourceUnavailable, this)); @@ -212,15 +212,15 @@ private PSRepositoryInfo UpdateRepositoryStoreHelper(string repoName, Uri repoUr } } - if(!isSecretManagementModuleAvailable) + if (!isSecretManagementModuleAvailable) { - WriteWarning($"Microsoft.PowerShell.SecretManagement module cannot be imported. Make sure it is available before performing PSResource operations in order to successfully authenticate to PSResourceRepository \"{repoName}\" with its CredentialInfo."); + WriteWarning($"Microsoft.PowerShell.SecretManagement module cannot be found. Make sure it is installed before performing PSResource operations in order to successfully authenticate to PSResourceRepository \"{repoName}\" with its CredentialInfo."); } } // determine if either 1 of 4 values are attempting to be set: URL, Priority, Trusted, CredentialInfo. // if none are (i.e only Name parameter was provided, write error) - if(repoUrl == null && repoPriority == DefaultPriority && _trustedNullable == null && repoCredentialInfo == null) + if (repoUrl == null && repoPriority == DefaultPriority && _trustedNullable == null && repoCredentialInfo == null) { throw new ArgumentException("Either URL, Priority, Trusted or CredentialInfo parameters must be requested to be set"); } @@ -286,22 +286,21 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) bool repoTrusted = false; isSet = false; - if(repo.ContainsKey("Trusted")) + if (repo.ContainsKey("Trusted")) { repoTrusted = (bool) repo["Trusted"]; isSet = true; } PSCredentialInfo repoCredentialInfo = null; - if(repo.ContainsKey("CredentialInfo")) { - if (!Utils.TryCreateValidPSCredentialInfo(credentialInfoCandidate: (PSObject) repo["CredentialInfo"], + if (repo.ContainsKey("CredentialInfo") && + !Utils.TryCreateValidPSCredentialInfo(credentialInfoCandidate: (PSObject) repo["CredentialInfo"], cmdletPassedIn: this, repoCredentialInfo: out repoCredentialInfo, errorRecord: out ErrorRecord errorRecord1)) - { - WriteError(errorRecord1); - return null; - } + { + WriteError(errorRecord1); + return null; } try diff --git a/src/code/Utils.cs b/src/code/Utils.cs index d35e4ae69..30e8765c2 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -285,11 +285,11 @@ public static bool TryCreateValidPSCredentialInfo( try { - if(!string.IsNullOrEmpty((string) credentialInfoCandidate.Properties[PSCredentialInfo.VaultNameAttribute]?.Value) + if (!string.IsNullOrEmpty((string) credentialInfoCandidate.Properties[PSCredentialInfo.VaultNameAttribute]?.Value) && !string.IsNullOrEmpty((string) credentialInfoCandidate.Properties[PSCredentialInfo.SecretNameAttribute]?.Value)) { PSCredential credential = null; - if(credentialInfoCandidate.Properties[PSCredentialInfo.CredentialAttribute] != null) + if (credentialInfoCandidate.Properties[PSCredentialInfo.CredentialAttribute] != null) { try { @@ -343,7 +343,7 @@ public static PSCredential GetRepositoryCredentialFromSecretManagement( PSCredentialInfo repositoryCredentialInfo, PSCmdlet cmdletPassedIn) { - if(!IsSecretManagementVaultAccessible(repositoryName, repositoryCredentialInfo, cmdletPassedIn)) + if (!IsSecretManagementVaultAccessible(repositoryName, repositoryCredentialInfo, cmdletPassedIn)) { cmdletPassedIn.ThrowTerminatingError( new ErrorRecord( @@ -383,9 +383,9 @@ public static PSCredential GetRepositoryCredentialFromSecretManagement( cmdletPassedIn)); } - if(secretValue is PSCredential) + if (secretValue is PSCredential secretCredential) { - return (PSCredential) secretValue; + return secretCredential; } cmdletPassedIn.ThrowTerminatingError( @@ -403,7 +403,7 @@ public static void SaveRepositoryCredentialToSecretManagementVault( PSCredentialInfo repositoryCredentialInfo, PSCmdlet cmdletPassedIn) { - if(!IsSecretManagementVaultAccessible(repositoryName, repositoryCredentialInfo, cmdletPassedIn)) + if (!IsSecretManagementVaultAccessible(repositoryName, repositoryCredentialInfo, cmdletPassedIn)) { cmdletPassedIn.ThrowTerminatingError( new ErrorRecord( diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index 9e6ebb5bf..2ecacdb7b 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -283,14 +283,14 @@ Describe "Test Register-PSResourceRepository" { It "prints a warning if CredentialInfo is passed in without SecretManagement module setup" { $output = Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo1 3>&1 - $output | Should -Match "Microsoft.PowerShell.SecretManagement module cannot be imported" + $output | Should -Match "Microsoft.PowerShell.SecretManagement module cannot be found" $res = Get-PSResourceRepository -Name $TestRepoName1 $res | Should -Not -BeNullOrEmpty } It "throws error if CredentialInfo is passed in with Credential property without SecretManagement module setup" { - { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo2 } | Should -Throw "Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" + { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo2 } | Should -Throw "Microsoft.PowerShell.SecretManagement module is not found, but is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" $res = Get-PSResourceRepository -Name $TestRepoName1 -ErrorAction Ignore $res | Should -BeNullOrEmpty diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 3d20a99b1..3830764b9 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -253,7 +253,7 @@ Describe "Test Set-PSResourceRepository" { It "prints a warning if CredentialInfo is passed in without SecretManagement module setup" { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path $output = Set-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -CredentialInfo $credentialInfo1 3>&1 - $output | Should -Match "Microsoft.PowerShell.SecretManagement module cannot be imported" + $output | Should -Match "Microsoft.PowerShell.SecretManagement module cannot be found" $res = Get-PSResourceRepository -Name $TestRepoName1 $res | Should -Not -BeNullOrEmpty @@ -263,7 +263,7 @@ Describe "Test Set-PSResourceRepository" { { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path Set-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -CredentialInfo $credentialInfo2 - } | Should -Throw "Microsoft.PowerShell.SecretManagement module is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" + } | Should -Throw "Microsoft.PowerShell.SecretManagement module is not found, but is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" $res = Get-PSResourceRepository -Name $TestRepoName1 -ErrorAction Ignore $res.CredentialInfo | Should -BeNullOrEmpty From 37e402cc5605ed6d304670df2bae7dbe8bfc74cd Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Thu, 13 Jan 2022 12:41:01 -0600 Subject: [PATCH 18/20] check for null values explicitly --- src/code/RepositorySettings.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/code/RepositorySettings.cs b/src/code/RepositorySettings.cs index 745230376..5ca3f6e64 100644 --- a/src/code/RepositorySettings.cs +++ b/src/code/RepositorySettings.cs @@ -194,14 +194,13 @@ public static PSRepositoryInfo Update(string repoName, Uri repoURL, int repoPrio } // Create CredentialInfo based on new values or whether it was empty to begin with - PSCredentialInfo thisCredentialInfo; - try - { - thisCredentialInfo = new PSCredentialInfo(node.Attribute(PSCredentialInfo.VaultNameAttribute)?.Value, node.Attribute(PSCredentialInfo.SecretNameAttribute)?.Value); - } - catch (ArgumentException) + PSCredentialInfo thisCredentialInfo = null; + if (node.Attribute(PSCredentialInfo.VaultNameAttribute)?.Value != null && + node.Attribute(PSCredentialInfo.SecretNameAttribute)?.Value != null) { - thisCredentialInfo = null; + thisCredentialInfo = new PSCredentialInfo( + node.Attribute(PSCredentialInfo.VaultNameAttribute).Value, + node.Attribute(PSCredentialInfo.SecretNameAttribute).Value); } updatedRepo = new PSRepositoryInfo(repoName, From 343b8202d7847762e5b9cdbfbf87117f758775ae Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Thu, 13 Jan 2022 16:23:17 -0600 Subject: [PATCH 19/20] check for errorid in tests --- test/PSCredentialInfo.Tests.ps1 | 8 ++++---- test/RegisterPSResourceRepository.Tests.ps1 | 2 +- test/SetPSResourceRepository.Tests.ps1 | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/PSCredentialInfo.Tests.ps1 b/test/PSCredentialInfo.Tests.ps1 index 13d549a56..f4b61c644 100644 --- a/test/PSCredentialInfo.Tests.ps1 +++ b/test/PSCredentialInfo.Tests.ps1 @@ -6,11 +6,11 @@ Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force Describe "Create PSCredentialInfo with VaultName and SecretName" -tags 'CI' { It "Verifies VaultName is not empty" { - { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("", "testsecret") } | Should -Throw "Invalid CredentialInfo, VaultName must be a non-empty string" + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("", "testsecret") } | Should -Throw -ErrorId "ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand" } It "Verifies SecretName is not empty" { - { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "") } | Should -Throw "Invalid CredentialInfo, SecretName must be a non-empty string" + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo ("testvault", "") } | Should -Throw -ErrorId "ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand" } It "Creates PSCredentialInfo successfully if VaultName and SecretName are non-empty" { @@ -42,13 +42,13 @@ Describe "Create PSCredentialInfo from a PSObject" -tags 'CI' { It "Throws if VaultName is null" { $customObject = New-Object PSObject - { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo $customObject } | Should -Throw "Invalid CredentialInfo, VaultName must be a non-empty string" + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo $customObject } | Should -Throw -ErrorId "ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand" } It "Throws if SecretName is null" { $customObject = New-Object PSObject $customObject | Add-Member -Name "VaultName" -Value "testvault" -MemberType NoteProperty - { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo $customObject } | Should -Throw "Invalid CredentialInfo, SecretName must be a non-empty string" + { New-Object Microsoft.PowerShell.PowerShellGet.UtilClasses.PSCredentialInfo $customObject } | Should -Throw -ErrorId "ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand" } It "Creates PSCredentialInfo successfully from PSObject with VaultName and SecretName" { diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index 2ecacdb7b..7fd83f084 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -290,7 +290,7 @@ Describe "Test Register-PSResourceRepository" { } It "throws error if CredentialInfo is passed in with Credential property without SecretManagement module setup" { - { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo2 } | Should -Throw "Microsoft.PowerShell.SecretManagement module is not found, but is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" + { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -Trusted -Priority 20 -CredentialInfo $credentialInfo2 } | Should -Throw -ErrorId "RepositoryCredentialSecretManagementUnavailableModule" $res = Get-PSResourceRepository -Name $TestRepoName1 -ErrorAction Ignore $res | Should -BeNullOrEmpty diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 3830764b9..ba38d53f7 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -263,7 +263,7 @@ Describe "Test Set-PSResourceRepository" { { Register-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path Set-PSResourceRepository -Name $TestRepoName1 -URL $tmpDir1Path -CredentialInfo $credentialInfo2 - } | Should -Throw "Microsoft.PowerShell.SecretManagement module is not found, but is required for saving PSResourceRepository $TestRepoName1's Credential in a vault" + } | Should -Throw -ErrorId "RepositoryCredentialSecretManagementUnavailableModule" $res = Get-PSResourceRepository -Name $TestRepoName1 -ErrorAction Ignore $res.CredentialInfo | Should -BeNullOrEmpty From 24e64c032755b4226b25bc2901f0eff60820b197 Mon Sep 17 00:00:00 2001 From: Cansu Erdogan Date: Fri, 14 Jan 2022 16:49:53 -0600 Subject: [PATCH 20/20] remove pester results file from gitignore, add new line --- .gitignore | 1 - test/PSCredentialInfo.Tests.ps1 | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 87bc2da72..557d7d4e0 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ src/code/obj srcOld/code/bin srcOld/code/obj out -result.pester.xml diff --git a/test/PSCredentialInfo.Tests.ps1 b/test/PSCredentialInfo.Tests.ps1 index f4b61c644..18775ef03 100644 --- a/test/PSCredentialInfo.Tests.ps1 +++ b/test/PSCredentialInfo.Tests.ps1 @@ -79,4 +79,4 @@ Describe "Create PSCredentialInfo from a PSObject" -tags 'CI' { $credentialInfo.Credential.GetNetworkCredential().Password | Should -Be "password" } -} \ No newline at end of file +}