Skip to content

Commit d0d345d

Browse files
Use authentication challenge for unauthenticated feed
Fix NRE when getting scope Fix call to realm Fix scope Add content to the webrequest Remove extra debug Fix error message
1 parent 28d241e commit d0d345d

File tree

1 file changed

+81
-2
lines changed

1 file changed

+81
-2
lines changed

src/code/ContainerRegistryServerAPICalls.cs

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
using System.Text;
2121
using System.Security.Cryptography;
2222
using System.Text.Json;
23+
using ResourceType = Microsoft.PowerShell.PSResourceGet.UtilClasses.ResourceType;
2324

2425
namespace Microsoft.PowerShell.PSResourceGet
2526
{
@@ -46,6 +47,7 @@ internal class ContainerRegistryServerAPICalls : ServerApiCall
4647
const string containerRegistryFindImageVersionUrlTemplate = "https://{0}/v2/{1}/tags/list"; // 0 - registry, 1 - repo(modulename)
4748
const string containerRegistryStartUploadTemplate = "https://{0}/v2/{1}/blobs/uploads/"; // 0 - registry, 1 - packagename
4849
const string containerRegistryEndUploadTemplate = "https://{0}{1}&digest=sha256:{2}"; // 0 - registry, 1 - location, 2 - digest
50+
const string defaultScope = "repository:*:*";
4951

5052
#endregion
5153

@@ -391,12 +393,18 @@ internal string GetContainerRegistryAccessToken(out ErrorRecord errRecord)
391393
}
392394
else
393395
{
394-
bool isRepositoryUnauthenticated = IsContainerRegistryUnauthenticated(Repository.Uri.ToString(), out errRecord);
396+
bool isRepositoryUnauthenticated = IsContainerRegistryUnauthenticated(Repository.Uri.ToString(), out errRecord, out accessToken);
395397
if (errRecord != null)
396398
{
397399
return null;
398400
}
399401

402+
if (!string.IsNullOrEmpty(accessToken))
403+
{
404+
_cmdletPassedIn.WriteVerbose("Anonymous access token retrieved.");
405+
return accessToken;
406+
}
407+
400408
if (!isRepositoryUnauthenticated)
401409
{
402410
accessToken = Utils.GetAzAccessToken();
@@ -436,15 +444,86 @@ internal string GetContainerRegistryAccessToken(out ErrorRecord errRecord)
436444
/// <summary>
437445
/// Checks if container registry repository is unauthenticated.
438446
/// </summary>
439-
internal bool IsContainerRegistryUnauthenticated(string containerRegistyUrl, out ErrorRecord errRecord)
447+
internal bool IsContainerRegistryUnauthenticated(string containerRegistyUrl, out ErrorRecord errRecord, out string anonymousAccessToken)
440448
{
441449
_cmdletPassedIn.WriteDebug("In ContainerRegistryServerAPICalls::IsContainerRegistryUnauthenticated()");
442450
errRecord = null;
451+
anonymousAccessToken = string.Empty;
443452
string endpoint = $"{containerRegistyUrl}/v2/";
444453
HttpResponseMessage response;
445454
try
446455
{
447456
response = _sessionClient.SendAsync(new HttpRequestMessage(HttpMethod.Head, endpoint)).Result;
457+
458+
if (response.StatusCode == HttpStatusCode.Unauthorized)
459+
{
460+
// check if there is a auth challenge header
461+
if (response.Headers.WwwAuthenticate.Count() > 0)
462+
{
463+
var authHeader = response.Headers.WwwAuthenticate.First();
464+
if (authHeader.Scheme == "Bearer")
465+
{
466+
// check if there is a realm
467+
if (authHeader.Parameter.Contains("realm"))
468+
{
469+
// get the realm
470+
var realm = authHeader.Parameter.Split(',')?.Where(x => x.Contains("realm"))?.FirstOrDefault()?.Split('=')[1]?.Trim('"');
471+
// get the service
472+
var service = authHeader.Parameter.Split(',')?.Where(x => x.Contains("service"))?.FirstOrDefault()?.Split('=')[1]?.Trim('"');
473+
474+
if (string.IsNullOrEmpty(realm) || string.IsNullOrEmpty(service))
475+
{
476+
errRecord = new ErrorRecord(
477+
new InvalidOperationException("Failed to get realm or service from the auth challenge header."),
478+
"RegistryUnauthenticationCheckError",
479+
ErrorCategory.InvalidResult,
480+
this);
481+
482+
return false;
483+
}
484+
485+
string content = "grant_type=access_token&service=" + service + "&scope=" + defaultScope;
486+
var contentHeaders = new Collection<KeyValuePair<string, string>> { new KeyValuePair<string, string>("Content-Type", "application/x-www-form-urlencoded") };
487+
488+
// get the anonymous access token
489+
var url = $"{realm}?service={service}&scope={defaultScope}";
490+
var results = GetHttpResponseJObjectUsingContentHeaders(url, HttpMethod.Get, content, contentHeaders, out errRecord);
491+
492+
if (errRecord != null)
493+
{
494+
_cmdletPassedIn.WriteDebug($"Failed to get access token from the realm. Error: {errRecord}");
495+
return false;
496+
}
497+
498+
if (results == null)
499+
{
500+
_cmdletPassedIn.WriteDebug("Failed to get access token from the realm. results is null.");
501+
return false;
502+
}
503+
504+
if (results["access_token"] == null)
505+
{
506+
_cmdletPassedIn.WriteDebug($"Failed to get access token from the realm. access_token is null. results: {results}");
507+
return false;
508+
}
509+
510+
anonymousAccessToken = results["access_token"].ToString();
511+
_cmdletPassedIn.WriteDebug("Anonymous access token retrieved");
512+
return true;
513+
}
514+
}
515+
}
516+
}
517+
}
518+
catch (HttpRequestException hre)
519+
{
520+
errRecord = new ErrorRecord(
521+
hre,
522+
"RegistryAnonymousAcquireError",
523+
ErrorCategory.ConnectionError,
524+
this);
525+
526+
return false;
448527
}
449528
catch (Exception e)
450529
{

0 commit comments

Comments
 (0)