diff --git a/src/code/RegisterPSResourceRepository.cs b/src/code/RegisterPSResourceRepository.cs index a31c110a8..d019071c6 100644 --- a/src/code/RegisterPSResourceRepository.cs +++ b/src/code/RegisterPSResourceRepository.cs @@ -34,6 +34,7 @@ class RegisterPSResourceRepository : PSCmdlet private const string NameParameterSet = "NameParameterSet"; private const string PSGalleryParameterSet = "PSGalleryParameterSet"; private const string RepositoriesParameterSet = "RepositoriesParameterSet"; + private Uri _url; #endregion @@ -51,25 +52,7 @@ class RegisterPSResourceRepository : PSCmdlet /// [Parameter(Mandatory = true, Position = 1, ParameterSetName = NameParameterSet)] [ValidateNotNullOrEmpty] - public Uri URL - { - get - { return _url; } - - set - { - if (!Uri.TryCreate(value, string.Empty, out Uri url)) - { - var message = string.Format(CultureInfo.InvariantCulture, "The URL provided is not valid: {0}", value); - var ex = new ArgumentException(message); - var moduleManifestNotFound = new ErrorRecord(ex, "InvalidUrl", ErrorCategory.InvalidArgument, null); - ThrowTerminatingError(moduleManifestNotFound); - } - - _url = url; - } - } - private Uri _url; + public string URL { get; set; } /// /// When specified, registers PSGallery repository. @@ -156,9 +139,17 @@ protected override void ProcessRecord() switch (ParameterSetName) { case NameParameterSet: + if (!Utils.TryCreateValidUrl(urlString: URL, + cmdletPassedIn: this, + urlResult: out _url, + errorRecord: out ErrorRecord errorRecord)) + { + ThrowTerminatingError(errorRecord); + } + try { - items.Add(NameParameterSetHelper(Name, URL, Priority, Trusted)); + items.Add(NameParameterSetHelper(Name, _url, Priority, Trusted)); } catch (Exception e) { @@ -334,13 +325,12 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) return null; } - if (!Uri.TryCreate(repo["URL"].ToString(), UriKind.Absolute, out Uri repoURL)) + if (!Utils.TryCreateValidUrl(urlString: repo["Url"].ToString(), + cmdletPassedIn: this, + urlResult: out Uri repoURL, + errorRecord: out ErrorRecord errorRecord)) { - WriteError(new ErrorRecord( - new PSInvalidOperationException("Invalid url, unable to create"), - "InvalidUrlScheme", - ErrorCategory.InvalidArgument, - this)); + WriteError(errorRecord); return null; } @@ -372,6 +362,6 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo) } } - #endregion + #endregion } } diff --git a/src/code/SetPSResourceRepository.cs b/src/code/SetPSResourceRepository.cs index c3f161333..5f674f02f 100644 --- a/src/code/SetPSResourceRepository.cs +++ b/src/code/SetPSResourceRepository.cs @@ -27,6 +27,7 @@ class SetPSResourceRepository : PSCmdlet private const string NameParameterSet = "NameParameterSet"; private const string RepositoriesParameterSet = "RepositoriesParameterSet"; private const int DefaultPriority = -1; + private Uri _url; #endregion #region Parameters @@ -45,25 +46,7 @@ class SetPSResourceRepository : PSCmdlet /// [Parameter(ParameterSetName = NameParameterSet)] [ValidateNotNullOrEmpty] - public Uri URL - { - get - { return _url; } - - set - { - if (!Uri.TryCreate(value, string.Empty, out Uri url)) - { - var message = string.Format(CultureInfo.InvariantCulture, "The URL provided is not a valid url: {0}", value); - var ex = new ArgumentException(message); - var urlErrorRecord = new ErrorRecord(ex, "InvalidUrl", ErrorCategory.InvalidArgument, null); - ThrowTerminatingError(urlErrorRecord); - } - - _url = url; - } - } - private Uri _url; + public string URL { get; set; } /// /// Specifies a hashtable of repositories and is used to register multiple repositories at once. @@ -129,6 +112,15 @@ protected override void BeginProcessing() protected override void ProcessRecord() { + if (MyInvocation.BoundParameters.ContainsKey(nameof(URL))) + { + bool isUrlValid = Utils.TryCreateValidUrl(URL, this, out _url, out ErrorRecord errorRecord); + if (!isUrlValid) + { + ThrowTerminatingError(errorRecord); + } + } + List items = new List(); switch(ParameterSetName) @@ -136,7 +128,7 @@ protected override void ProcessRecord() case NameParameterSet: try { - items.Add(UpdateRepositoryStoreHelper(Name, URL, Priority, Trusted)); + items.Add(UpdateRepositoryStoreHelper(Name, _url, Priority, Trusted)); } catch (Exception e) { @@ -243,15 +235,28 @@ private List RepositoriesParameterSetHelper() private PSRepositoryInfo RepoValidationHelper(Hashtable repo) { WriteDebug(String.Format("Parsing through repository: {0}", repo["Name"])); + Uri repoURL = null; - if (repo.ContainsKey("Url") && !Uri.TryCreate(repo["URL"].ToString(), UriKind.Absolute, out repoURL)) + if (repo.ContainsKey("Url")) { - WriteError(new ErrorRecord( - new PSInvalidOperationException("Invalid Url, unable to parse and create Uri"), - "InvalidUrl", - ErrorCategory.InvalidArgument, - this)); - return null; + if (String.IsNullOrEmpty(repo["Url"].ToString())) + { + WriteError(new ErrorRecord( + new PSInvalidOperationException("Repository url cannot be null if provided"), + "NullURLForRepositoriesParameterSetUpdate", + ErrorCategory.InvalidArgument, + this)); + return null; + } + + if (!Utils.TryCreateValidUrl(urlString: repo["Url"].ToString(), + cmdletPassedIn: this, + urlResult: out repoURL, + errorRecord: out ErrorRecord errorRecord)) + { + WriteError(errorRecord); + return null; + } } bool repoTrusted = false; diff --git a/src/code/Utils.cs b/src/code/Utils.cs index b36e80130..492d07025 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -11,6 +11,7 @@ using System.Management.Automation.Language; using System.Runtime.InteropServices; using NuGet.Versioning; +using System.Globalization; namespace Microsoft.PowerShell.PowerShellGet.UtilClasses { @@ -171,6 +172,55 @@ public static bool TryParseVersionOrVersionRange( return VersionRange.TryParse(version, out versionRange); } + #endregion + + #region Url methods + + public static bool TryCreateValidUrl( + string urlString, + PSCmdlet cmdletPassedIn, + out Uri urlResult, + out ErrorRecord errorRecord + ) + { + errorRecord = null; + + if (!urlString.StartsWith(Uri.UriSchemeHttps) && + !urlString.StartsWith(Uri.UriSchemeHttp) && + !urlString.StartsWith(Uri.UriSchemeFtp)) + { + // url string could be of type (potentially) UriSchemeFile or invalid type + // can't check for UriSchemeFile because relative paths don't qualify as UriSchemeFile + try + { + // this is needed for a relative path urlstring. Does not throw error for an absolute path + urlString = cmdletPassedIn.SessionState.Path.GetResolvedPSPathFromPSPath(urlString)[0].Path; + + } + catch (Exception) + { + // this should only be reached if the url string is invalid + // i.e www.google.com + var message = string.Format(CultureInfo.InvariantCulture, "The URL provided is not valid: {0} and must be of Uri Scheme: HTTP, HTTPS, FTP or File", urlString); + var ex = new ArgumentException(message); + errorRecord = new ErrorRecord(ex, "InvalidUrl", ErrorCategory.InvalidArgument, null); + urlResult = null; + return false; + } + } + + bool tryCreateResult = Uri.TryCreate(urlString, UriKind.Absolute, out urlResult); + if (!tryCreateResult) + { + var message = string.Format(CultureInfo.InvariantCulture, "The URL provided is not valid: {0}", urlString); + var ex = new ArgumentException(message); + errorRecord = new ErrorRecord(ex, "InvalidUrl", ErrorCategory.InvalidArgument, null); + urlResult = null; + } + + return tryCreateResult; + } + #endregion #region Path methods diff --git a/test/RegisterPSResourceRepository.Tests.ps1 b/test/RegisterPSResourceRepository.Tests.ps1 index 58c1e4c90..fd4204774 100644 --- a/test/RegisterPSResourceRepository.Tests.ps1 +++ b/test/RegisterPSResourceRepository.Tests.ps1 @@ -13,6 +13,8 @@ Describe "Test Register-PSResourceRepository" { $tmpDir3Path = Join-Path -Path $TestDrive -ChildPath "tmpDir3" $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path) Get-NewTestDirs($tmpDirPaths) + + $relativeCurrentPath = Get-Location } AfterEach { Get-RevertPSResourceRepositoryFile @@ -26,7 +28,7 @@ Describe "Test Register-PSResourceRepository" { It "register repository given Name, URL (bare minimum for NameParmaterSet)" { $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -PassThru $res.Name | Should -Be "testRepository" - $res.URL | Should -Contain $tmpDir1Path + $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Trusted | Should -Be False $res.Priority | Should -Be 50 } @@ -34,7 +36,7 @@ Describe "Test Register-PSResourceRepository" { It "register repository with Name, URL, Trusted (NameParameterSet)" { $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -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 50 } @@ -42,7 +44,7 @@ Describe "Test Register-PSResourceRepository" { It "register repository given Name, URL, Trusted, Priority (NameParameterSet)" { $res = Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path -Trusted -Priority 20 -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 } @@ -82,17 +84,17 @@ Describe "Test Register-PSResourceRepository" { Register-PSResourceRepository -Repositories $arrayOfHashtables $res = Get-PSResourceRepository -Name "testRepository" - $res.URL | Should -Contain $tmpDir1Path + $res.URL.LocalPath | Should -Contain $tmpDir1Path $res.Trusted | Should -Be False $res.Priority | Should -Be 50 $res2 = Get-PSResourceRepository -Name "testRepository2" - $res2.URL | Should -Contain $tmpDir2Path + $res2.URL.LocalPath | Should -Contain $tmpDir2Path $res2.Trusted | Should -Be True $res2.Priority | Should -Be 50 $res3 = Get-PSResourceRepository -Name "testRepository3" - $res3.URL | Should -Contain $tmpDir3Path + $res3.URL.LocalPath | Should -Contain $tmpDir3Path $res3.Trusted | Should -Be True $res3.Priority | Should -Be 20 } @@ -123,23 +125,23 @@ Describe "Test Register-PSResourceRepository" { $res1.Priority | Should -Be 50 $res2 = Get-PSResourceRepository -Name "testRepository" - $res2.URL | Should -Contain $tmpDir1Path + $res2.URL.LocalPath | Should -Contain $tmpDir1Path $res2.Trusted | Should -Be False $res2.Priority | Should -Be 50 $res3 = Get-PSResourceRepository -Name "testRepository2" - $res3.URL | Should -Contain $tmpDir2Path + $res3.URL.LocalPath | Should -Contain $tmpDir2Path $res3.Trusted | Should -Be True $res3.Priority | Should -Be 50 $res4 = Get-PSResourceRepository -Name "testRepository3" - $res4.URL | Should -Contain $tmpDir3Path + $res4.URL.LocalPath | Should -Contain $tmpDir3Path $res4.Trusted | Should -Be True $res4.Priority | Should -Be 20 } It "not register repository when Name is provided but URL is not" { - {Register-PSResourceRepository -Name "testRepository" -URL "" -ErrorAction Stop} | Should -Throw -ErrorId "InvalidUrl,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" + {Register-PSResourceRepository -Name "testRepository" -URL "" -ErrorAction Stop} | Should -Throw -ErrorId "ParameterArgumentValidationError,Microsoft.PowerShell.PowerShellGet.Cmdlets.RegisterPSResourceRepository" } It "not register repository when Name is empty but URL is provided" { @@ -193,7 +195,7 @@ Describe "Test Register-PSResourceRepository" { $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 = "InvalidUrlScheme,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"} It "not register incorrectly formatted Name type repo among correct ones when incorrect type is " -TestCases $testCases2 { param($Type, $IncorrectHashTable, $ErrorId) @@ -218,4 +220,14 @@ Describe "Test Register-PSResourceRepository" { $res3.Name | Should -Be "PSGallery" $res3.Priority | Should -Be 30 } + + It "should register repository with relative location provided as URL" { + Register-PSResourceRepository -Name "testRepository" -URL "./" + $res = Get-PSResourceRepository -Name "testRepository" + + $res.Name | Should -Be "testRepository" + $res.URL.LocalPath | Should -Contain $relativeCurrentPath + $res.Trusted | Should -Be False + $res.Priority | Should -Be 50 + } } diff --git a/test/SetPSResourceRepository.Tests.ps1 b/test/SetPSResourceRepository.Tests.ps1 index 24cb5dfc3..c302c3d79 100644 --- a/test/SetPSResourceRepository.Tests.ps1 +++ b/test/SetPSResourceRepository.Tests.ps1 @@ -3,7 +3,7 @@ Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force -Describe "Test Register-PSResourceRepository" { +Describe "Test Set-PSResourceRepository" { BeforeEach { $PSGalleryName = Get-PSGalleryName $PSGalleryURL = Get-PSGalleryLocation @@ -13,6 +13,8 @@ Describe "Test Register-PSResourceRepository" { $tmpDir3Path = Join-Path -Path $TestDrive -ChildPath "tmpDir3" $tmpDirPaths = @($tmpDir1Path, $tmpDir2Path, $tmpDir3Path) Get-NewTestDirs($tmpDirPaths) + + $relativeCurrentPath = Get-Location } AfterEach { Get-RevertPSResourceRepositoryFile @@ -28,7 +30,7 @@ Describe "Test Register-PSResourceRepository" { Set-PSResourceRepository -Name "testRepository" -URL $tmpDir2Path $res = Get-PSResourceRepository -Name "testRepository" $res.Name | Should -Be "testRepository" - $res.URL | Should -Contain $tmpDir2Path + $res.URL.LocalPath | Should -Contain $tmpDir2Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False } @@ -38,7 +40,7 @@ Describe "Test Register-PSResourceRepository" { Set-PSResourceRepository -Name "testRepository" -Priority 25 $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 25 $res.Trusted | Should -Be False } @@ -48,7 +50,7 @@ Describe "Test Register-PSResourceRepository" { Set-PSResourceRepository -Name "testRepository" -Trusted $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 True } @@ -59,8 +61,8 @@ Describe "Test Register-PSResourceRepository" { } $testCases = @{Type = "contains *"; Name = "test*Repository"; ErrorId = "ErrorInNameParameterSet"}, - @{Type = "is whitespace"; Name = " "; ErrorId = "ErrorInNameParameterSet"}, - @{Type = "is null"; Name = $null; ErrorId = "ParameterArgumentValidationError"} + @{Type = "is whitespace"; Name = " "; ErrorId = "ErrorInNameParameterSet"}, + @{Type = "is null"; Name = $null; ErrorId = "ParameterArgumentValidationError"} It "not set repository and throw error given Name (NameParameterSet)" -TestCases $testCases { param($Type, $Name) @@ -88,7 +90,7 @@ Describe "Test Register-PSResourceRepository" { $err[0].FullyQualifiedErrorId | Should -BeExactly "$ErrorId,Microsoft.PowerShell.PowerShellGet.Cmdlets.SetPSResourceRepository" $res = Get-PSResourceRepository -Name "testRepository" - $res.URL | Should -Contain $tmpDir3Path + $res.URL.LocalPath | Should -Contain $tmpDir3Path $res.Trusted | Should -Be False $res2 = Get-PSResourceRepository -Name "testRepository2" @@ -110,13 +112,13 @@ Describe "Test Register-PSResourceRepository" { Set-PSResourceRepository -Repositories $arrayOfHashtables $res = Get-PSResourceRepository -Name "testRepository1" $res.Name | Should -Be "testRepository1" - $res.URL | Should -Contain $tmpDir2Path + $res.URL.LocalPath | Should -Contain $tmpDir2Path $res.Priority | Should -Be 50 $res.Trusted | Should -Be False $res2 = Get-PSResourceRepository -Name "testRepository2" $res2.Name | Should -Be "testRepository2" - $res2.URL | Should -Contain $tmpDir2Path + $res2.URL.LocalPath | Should -Contain $tmpDir2Path $res2.Priority | Should -Be 25 $res2.Trusted | Should -Be False @@ -148,8 +150,18 @@ Describe "Test Register-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 } + + It "should set repository with relative URL provided" { + Register-PSResourceRepository -Name "testRepository" -URL $tmpDir1Path + Set-PSResourceRepository -Name "testRepository" -URL $relativeCurrentPath + $res = Get-PSResourceRepository -Name "testRepository" + $res.Name | Should -Be "testRepository" + $res.URL.LocalPath | Should -Contain $relativeCurrentPath + $res.Trusted | Should -Be False + $res.Priority | Should -Be 50 + } }