@@ -47,6 +47,10 @@ internal class ContainerRegistryServerAPICalls : ServerApiCall
4747 const string containerRegistryStartUploadTemplate = "https://{0}/v2/{1}/blobs/uploads/" ; // 0 - registry, 1 - packagename
4848 const string containerRegistryEndUploadTemplate = "https://{0}{1}&digest=sha256:{2}" ; // 0 - registry, 1 - location, 2 - digest
4949 const string defaultScope = "&scope=repository:*:*&scope=registry:catalog:*" ;
50+ const string catalogScope = "&scope=registry:catalog:*" ;
51+ const string grantTypeTemplate = "grant_type=access_token&service={0}{1}" ; // 0 - registry, 1 - scope
52+ const string authUrlTemplate = "{0}?service={1}{2}" ; // 0 - realm, 1 - service, 2 - scope
53+
5054 const string containerRegistryRepositoryListTemplate = "https://{0}/v2/_catalog" ; // 0 - registry
5155
5256 #endregion
@@ -323,7 +327,7 @@ private Stream InstallVersion(
323327 return null ;
324328 }
325329
326- string containerRegistryAccessToken = GetContainerRegistryAccessToken ( out errRecord ) ;
330+ string containerRegistryAccessToken = GetContainerRegistryAccessToken ( needCatalogAccess : false , out errRecord ) ;
327331 if ( errRecord != null )
328332 {
329333 return null ;
@@ -371,7 +375,7 @@ private Stream InstallVersion(
371375 /// If no credential provided at registration then, check if the ACR endpoint can be accessed without a token. If not, try using Azure.Identity to get the az access token, then ACR refresh token and then ACR access token.
372376 /// Note: Access token can be empty if the repository is unauthenticated
373377 /// </summary>
374- internal string GetContainerRegistryAccessToken ( out ErrorRecord errRecord )
378+ internal string GetContainerRegistryAccessToken ( bool needCatalogAccess , out ErrorRecord errRecord )
375379 {
376380 _cmdletPassedIn . WriteDebug ( "In ContainerRegistryServerAPICalls::GetContainerRegistryAccessToken()" ) ;
377381 string accessToken = string . Empty ;
@@ -393,7 +397,7 @@ internal string GetContainerRegistryAccessToken(out ErrorRecord errRecord)
393397 }
394398 else
395399 {
396- bool isRepositoryUnauthenticated = IsContainerRegistryUnauthenticated ( Repository . Uri . ToString ( ) , out errRecord , out accessToken ) ;
400+ bool isRepositoryUnauthenticated = IsContainerRegistryUnauthenticated ( Repository . Uri . ToString ( ) , needCatalogAccess , out errRecord , out accessToken ) ;
397401 _cmdletPassedIn . WriteDebug ( $ "Is repository unauthenticated: { isRepositoryUnauthenticated } ") ;
398402
399403 if ( errRecord != null )
@@ -446,7 +450,7 @@ internal string GetContainerRegistryAccessToken(out ErrorRecord errRecord)
446450 /// <summary>
447451 /// Checks if container registry repository is unauthenticated.
448452 /// </summary>
449- internal bool IsContainerRegistryUnauthenticated ( string containerRegistyUrl , out ErrorRecord errRecord , out string anonymousAccessToken )
453+ internal bool IsContainerRegistryUnauthenticated ( string containerRegistyUrl , bool needCatalogAccess , out ErrorRecord errRecord , out string anonymousAccessToken )
450454 {
451455 _cmdletPassedIn . WriteDebug ( "In ContainerRegistryServerAPICalls::IsContainerRegistryUnauthenticated()" ) ;
452456 errRecord = null ;
@@ -484,20 +488,24 @@ internal bool IsContainerRegistryUnauthenticated(string containerRegistyUrl, out
484488 return false ;
485489 }
486490
487- string content = "grant_type=access_token&service=" + service + defaultScope ;
491+ string content = needCatalogAccess ? String . Format ( grantTypeTemplate , service , catalogScope ) : String . Format ( grantTypeTemplate , service , defaultScope ) ;
492+
488493 var contentHeaders = new Collection < KeyValuePair < string , string > > { new KeyValuePair < string , string > ( "Content-Type" , "application/x-www-form-urlencoded" ) } ;
489494
490- // get the anonymous access token
491- var url = $ "{ realm } ?service={ service } { defaultScope } ";
495+ string url = needCatalogAccess ? String . Format ( authUrlTemplate , realm , service , catalogScope ) : String . Format ( authUrlTemplate , realm , service , defaultScope ) ;
492496
493497 _cmdletPassedIn . WriteDebug ( $ "Getting anonymous access token from the realm: { url } ") ;
494498
495499 // we dont check the errorrecord here because we want to return false if we get a 401 and not throw an error
496- var results = GetHttpResponseJObjectUsingContentHeaders ( url , HttpMethod . Get , content , contentHeaders , out _ ) ;
500+ _cmdletPassedIn . WriteDebug ( $ "Getting anonymous access token from the realm: { url } ") ;
501+ ErrorRecord errRecordTemp = null ;
502+
503+ var results = GetHttpResponseJObjectUsingContentHeaders ( url , HttpMethod . Get , content , contentHeaders , out errRecordTemp ) ;
497504
498505 if ( results == null )
499506 {
500507 _cmdletPassedIn . WriteDebug ( "Failed to get access token from the realm. results is null." ) ;
508+ _cmdletPassedIn . WriteDebug ( $ "ErrorRecord: { errRecordTemp } ") ;
501509 return false ;
502510 }
503511
@@ -508,7 +516,6 @@ internal bool IsContainerRegistryUnauthenticated(string containerRegistyUrl, out
508516 }
509517
510518 anonymousAccessToken = results [ "access_token" ] . ToString ( ) ;
511- _cmdletPassedIn . WriteDebug ( "Anonymous access token retrieved" ) ;
512519 return true ;
513520 }
514521 }
@@ -761,7 +768,7 @@ internal Hashtable GetContainerRegistryMetadata(string packageName, string exact
761768 if ( ! NuGetVersion . TryParse ( pkgVersionString , out NuGetVersion pkgVersion ) )
762769 {
763770 errRecord = new ErrorRecord (
764- new ArgumentException ( $ "Version { pkgVersionString } to be parsed from metadata is not a valid NuGet version.") ,
771+ new ArgumentException ( $ "Version { pkgVersionString } to be parsed from metadata is not a valid NuGet version for package ' { packageName } ' .") ,
765772 "ParseMetadataFailure" ,
766773 ErrorCategory . InvalidArgument ,
767774 this ) ;
@@ -988,24 +995,29 @@ internal JObject GetHttpResponseJObjectUsingContentHeaders(string url, HttpMetho
988995 {
989996 HttpRequestMessage request = new HttpRequestMessage ( method , url ) ;
990997
991- if ( string . IsNullOrEmpty ( content ) )
998+ // HTTP GET does not expect a body / content.
999+ if ( method != HttpMethod . Get )
9921000 {
993- errRecord = new ErrorRecord (
994- exception : new ArgumentNullException ( $ "Content is null or empty and cannot be used to make a request as its content headers.") ,
995- "RequestContentHeadersNullOrEmpty" ,
996- ErrorCategory . InvalidData ,
997- _cmdletPassedIn ) ;
9981001
999- return null ;
1000- }
1002+ if ( string . IsNullOrEmpty ( content ) )
1003+ {
1004+ errRecord = new ErrorRecord (
1005+ exception : new ArgumentNullException ( $ "Content is null or empty and cannot be used to make a request as its content headers.") ,
1006+ "RequestContentHeadersNullOrEmpty" ,
1007+ ErrorCategory . InvalidData ,
1008+ _cmdletPassedIn ) ;
10011009
1002- request . Content = new StringContent ( content ) ;
1003- request . Content . Headers . Clear ( ) ;
1004- if ( contentHeaders != null )
1005- {
1006- foreach ( var header in contentHeaders )
1010+ return null ;
1011+ }
1012+
1013+ request . Content = new StringContent ( content ) ;
1014+ request . Content . Headers . Clear ( ) ;
1015+ if ( contentHeaders != null )
10071016 {
1008- request . Content . Headers . Add ( header . Key , header . Value ) ;
1017+ foreach ( var header in contentHeaders )
1018+ {
1019+ request . Content . Headers . Add ( header . Key , header . Value ) ;
1020+ }
10091021 }
10101022 }
10111023
@@ -1234,7 +1246,7 @@ internal bool PushNupkgContainerRegistry(
12341246
12351247 // Get access token (includes refresh tokens)
12361248 _cmdletPassedIn . WriteVerbose ( $ "Get access token for container registry server.") ;
1237- var containerRegistryAccessToken = GetContainerRegistryAccessToken ( out errRecord ) ;
1249+ var containerRegistryAccessToken = GetContainerRegistryAccessToken ( needCatalogAccess : false , out errRecord ) ;
12381250 if ( errRecord != null )
12391251 {
12401252 return false ;
@@ -1699,7 +1711,7 @@ private Hashtable[] FindPackagesWithVersionHelper(string packageName, VersionTyp
16991711 string packageNameLowercase = packageName . ToLower ( ) ;
17001712
17011713 string packageNameForFind = PrependMARPrefix ( packageNameLowercase ) ;
1702- string containerRegistryAccessToken = GetContainerRegistryAccessToken ( out errRecord ) ;
1714+ string containerRegistryAccessToken = GetContainerRegistryAccessToken ( needCatalogAccess : false , out errRecord ) ;
17031715 if ( errRecord != null )
17041716 {
17051717 return emptyHashResponses ;
@@ -1715,8 +1727,9 @@ private Hashtable[] FindPackagesWithVersionHelper(string packageName, VersionTyp
17151727 List < JToken > allVersionsList = foundTags [ "tags" ] . ToList ( ) ;
17161728
17171729 SortedDictionary < NuGet . Versioning . SemanticVersion , string > sortedQualifyingPkgs = GetPackagesWithRequiredVersion ( allVersionsList , versionType , versionRange , requiredVersion , packageNameForFind , includePrerelease , out errRecord ) ;
1718- if ( errRecord != null )
1730+ if ( errRecord != null && sortedQualifyingPkgs ? . Count == 0 )
17191731 {
1732+ _cmdletPassedIn . WriteDebug ( "No qualifying packages found for the specified criteria." ) ;
17201733 return emptyHashResponses ;
17211734 }
17221735
@@ -1760,12 +1773,14 @@ private Hashtable[] FindPackagesWithVersionHelper(string packageName, VersionTyp
17601773 if ( ! NuGetVersion . TryParse ( pkgVersionString , out NuGetVersion pkgVersion ) )
17611774 {
17621775 errRecord = new ErrorRecord (
1763- new ArgumentException ( $ "Version { pkgVersionString } to be parsed from metadata is not a valid NuGet version.") ,
1776+ new ArgumentException ( $ "Version { pkgVersionString } to be parsed from metadata is not a valid NuGet version for package ' { packageName } ' .") ,
17641777 "FindNameFailure" ,
17651778 ErrorCategory . InvalidArgument ,
17661779 this ) ;
17671780
1768- return null ;
1781+ _cmdletPassedIn . WriteError ( errRecord ) ;
1782+ _cmdletPassedIn . WriteDebug ( $ "Skipping package '{ packageName } ' with version '{ pkgVersionString } ' as it is not a valid NuGet version.") ;
1783+ continue ; // skip this version and continue with the next one
17691784 }
17701785
17711786 _cmdletPassedIn . WriteDebug ( $ "'{ packageName } ' version parsed as '{ pkgVersion } '") ;
@@ -1808,7 +1823,7 @@ private FindResults FindPackages(string packageName, bool includePrerelease, out
18081823 {
18091824 _cmdletPassedIn . WriteDebug ( "In ContainerRegistryServerAPICalls::FindPackages()" ) ;
18101825 errRecord = null ;
1811- string containerRegistryAccessToken = GetContainerRegistryAccessToken ( out errRecord ) ;
1826+ string containerRegistryAccessToken = GetContainerRegistryAccessToken ( needCatalogAccess : true , out errRecord ) ;
18121827 if ( errRecord != null )
18131828 {
18141829 return emptyResponseResults ;
0 commit comments