Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Commit

Permalink
Merge branch 'rel/v1.18' into updateversion4
Browse files Browse the repository at this point in the history
  • Loading branch information
jeschu1 authored Nov 2, 2018
2 parents 52d0609 + 0e10986 commit a8dc642
Show file tree
Hide file tree
Showing 6 changed files with 172 additions and 116 deletions.
206 changes: 103 additions & 103 deletions AzureDevOps.Authentication/Src/Authentication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -177,148 +177,148 @@ public override async Task<bool> DeleteCredentials(TargetUri targetUri)
if (targetUri is null)
throw new ArgumentNullException(nameof(targetUri));

// Assume Azure DevOps using Azure "common tenant" (empty GUID).
var tenantId = Guid.Empty;

// Compose the request Uri, by default it is the target Uri.
var requestUri = targetUri;

// Override the request Uri, when actual Uri exists, with actual Uri.
if (targetUri.ActualUri != null)
if (IsAzureDevOpsUrl(targetUri))
{
requestUri = targetUri.CreateWith(queryUri: targetUri.ActualUri);
}
// Assume Azure DevOps using Azure "common tenant" (empty GUID).
var tenantId = Guid.Empty;

// If the protocol (aka scheme) being used isn't HTTP based, there's no point in
// querying the server, so skip that work.
if (OrdinalIgnoreCase.Equals(requestUri.Scheme, Uri.UriSchemeHttp)
|| OrdinalIgnoreCase.Equals(requestUri.Scheme, Uri.UriSchemeHttps))
{
var requestUrl = GetTargetUrl(requestUri, false);
// Compose the request Uri, by default it is the target Uri.
var requestUri = targetUri;

// Read the cache from disk.
var cache = await DeserializeTenantCache(context);
// Override the request Uri, when actual Uri exists, with actual Uri.
if (targetUri.ActualUri != null)
{
requestUri = targetUri.CreateWith(queryUri: targetUri.ActualUri);
}

// Check the cache for an existing value.
if (cache.TryGetValue(requestUrl, out tenantId))
// If the protocol (aka scheme) being used isn't HTTP based, there's no point in
// querying the server, so skip that work.
if (OrdinalIgnoreCase.Equals(requestUri.Scheme, Uri.UriSchemeHttp)
|| OrdinalIgnoreCase.Equals(requestUri.Scheme, Uri.UriSchemeHttps))
{
context.Trace.WriteLine($"'{requestUrl}' is Azure DevOps, tenant resource is {{{tenantId.ToString("N")}}}.");
var requestUrl = GetTargetUrl(requestUri, false);

return tenantId;
}
// Read the cache from disk.
var cache = await DeserializeTenantCache(context);

// Use the properly formatted URL
requestUri = requestUri.CreateWith(queryUrl: requestUrl);
// Check the cache for an existing value.
if (cache.TryGetValue(requestUrl, out tenantId))
{
context.Trace.WriteLine($"'{requestUrl}' is Azure DevOps, tenant resource is {{{tenantId.ToString("N")}}}.");

var options = new NetworkRequestOptions(false)
{
Flags = NetworkRequestOptionFlags.UseProxy,
Timeout = TimeSpan.FromMilliseconds(Global.RequestTimeout),
};
return tenantId;
}

try
{
// Query the host use the response headers to determine if the host is Azure DevOps or not.
using (var response = await context.Network.HttpHeadAsync(requestUri, options))
// Use the properly formatted URL
requestUri = requestUri.CreateWith(queryUrl: requestUrl);

var options = new NetworkRequestOptions(false)
{
if (response.Headers != null)
Flags = NetworkRequestOptionFlags.UseProxy,
Timeout = TimeSpan.FromMilliseconds(Global.RequestTimeout),
};

try
{
// Query the host use the response headers to determine if the host is Azure DevOps or not.
using (var response = await context.Network.HttpHeadAsync(requestUri, options))
{
// If the "X-VSS-ResourceTenant" was returned, then it is Azure DevOps and we'll need it's value.
if (response.Headers.TryGetValues(XvssResourceTenantHeader, out IEnumerable<string> values))
if (response.Headers != null)
{
context.Trace.WriteLine($"detected '{requestUrl}' as Azure DevOps from GET response.");

// The "Www-Authenticate" is a more reliable header, because it indicates the
// authentication scheme that should be used to access the requested entity.
if (response.Headers.WwwAuthenticate != null)
// If the "X-VSS-ResourceTenant" was returned, then it is Azure DevOps and we'll need it's value.
if (response.Headers.TryGetValues(XvssResourceTenantHeader, out IEnumerable<string> values))
{
foreach (var header in response.Headers.WwwAuthenticate)
{
const string AuthorizationUriPrefix = "authorization_uri=";

var value = header.Parameter;
context.Trace.WriteLine($"detected '{requestUrl}' as Azure DevOps from GET response.");

if (value.Length >= AuthorizationUriPrefix.Length + AuthorityHostUrlBase.Length + GuidStringLength)
// The "Www-Authenticate" is a more reliable header, because it indicates the
// authentication scheme that should be used to access the requested entity.
if (response.Headers.WwwAuthenticate != null)
{
foreach (var header in response.Headers.WwwAuthenticate)
{
// The header parameter will look something like "authorization_uri=https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47"
// and all we want is the portion after the '=' and before the last '/'.
int index1 = value.IndexOf('=', AuthorizationUriPrefix.Length - 1);
int index2 = value.LastIndexOf('/');
const string AuthorizationUriPrefix = "authorization_uri=";

// Parse the header value if the necessary characters exist...
if (index1 > 0 && index2 > index1)
var value = header.Parameter;

if (value?.Length >= AuthorizationUriPrefix.Length + AuthorityHostUrlBase.Length + GuidStringLength)
{
var authorityUrl = value.Substring(index1 + 1, index2 - index1 - 1);
var guidString = value.Substring(index2 + 1, GuidStringLength);
// The header parameter will look something like "authorization_uri=https://login.microsoftonline.com/72f988bf-86f1-41af-91ab-2d7cd011db47"
// and all we want is the portion after the '=' and before the last '/'.
int index1 = value.IndexOf('=', AuthorizationUriPrefix.Length - 1);
int index2 = value.LastIndexOf('/');

// If the authority URL is as expected, attempt to parse the tenant resource identity.
if (OrdinalIgnoreCase.Equals(authorityUrl, AuthorityHostUrlBase)
&& Guid.TryParse(guidString, out tenantId))
// Parse the header value if the necessary characters exist...
if (index1 > 0 && index2 > index1)
{
// Update the cache.
cache[requestUrl] = tenantId;

// Write the cache to disk.
await SerializeTenantCache(context, cache);

// Since we found a value, break the loop (likely a loop of one item anyways).
break;
var authorityUrl = value.Substring(index1 + 1, index2 - index1 - 1);
var guidString = value.Substring(index2 + 1, GuidStringLength);

// If the authority URL is as expected, attempt to parse the tenant resource identity.
if (OrdinalIgnoreCase.Equals(authorityUrl, AuthorityHostUrlBase)
&& Guid.TryParse(guidString, out tenantId))
{
// Update the cache.
cache[requestUrl] = tenantId;

// Write the cache to disk.
await SerializeTenantCache(context, cache);

// Since we found a value, break the loop (likely a loop of one item anyways).
break;
}
}
}
}
}
}
else
{
// Since there wasn't a "Www-Authenticate" header returned
// iterate through the values, taking the first non-zero value.
foreach (string value in values)
else
{
// Try to find a value for the resource-tenant identity.
// Given that some projects will return multiple tenant identities,
if (!string.IsNullOrWhiteSpace(value)
&& Guid.TryParse(value, out tenantId))
// Since there wasn't a "Www-Authenticate" header returned
// iterate through the values, taking the first non-zero value.
foreach (string value in values)
{
// Update the cache.
cache[requestUrl] = tenantId;
// Try to find a value for the resource-tenant identity.
// Given that some projects will return multiple tenant identities,
if (!string.IsNullOrWhiteSpace(value)
&& Guid.TryParse(value, out tenantId))
{
// Update the cache.
cache[requestUrl] = tenantId;

// Write the cache to disk.
await SerializeTenantCache(context, cache);
// Write the cache to disk.
await SerializeTenantCache(context, cache);

// Break the loop if a non-zero value has been detected.
if (tenantId != Guid.Empty)
{
break;
// Break the loop if a non-zero value has been detected.
if (tenantId != Guid.Empty)
{
break;
}
}
}
}
}

context.Trace.WriteLine($"tenant resource for '{requestUrl}' is {{{tenantId.ToString("N")}}}.");
context.Trace.WriteLine($"tenant resource for '{requestUrl}' is {{{tenantId.ToString("N")}}}.");

// Return the tenant identity to the caller because this is Azure DevOps.
return tenantId;
// Return the tenant identity to the caller because this is Azure DevOps.
return tenantId;
}
}
else
{
context.Trace.WriteLine($"unable to get response from '{requestUri}' [{(int)response.StatusCode} {response.StatusCode}].");
}
}
else
{
context.Trace.WriteLine($"unable to get response from '{requestUri}' [{(int)response.StatusCode} {response.StatusCode}].");
}
}
catch (HttpRequestException exception)
{
context.Trace.WriteLine($"unable to get response from '{requestUri}', an error occurred before the server could respond.");
context.Trace.WriteException(exception);
}
}
catch (HttpRequestException exception)
else
{
context.Trace.WriteLine($"unable to get response from '{requestUri}', an error occurred before the server could respond.");
context.Trace.WriteException(exception);
context.Trace.WriteLine($"detected non-http(s) based protocol: '{requestUri.Scheme}'.");
}
}
else
{
context.Trace.WriteLine($"detected non-http(s) based protocol: '{requestUri.Scheme}'.");
}

if (OrdinalIgnoreCase.Equals(VstsBaseUrlHost, requestUri.Host))
return Guid.Empty;

// Fallback to basic authentication.
return null;
Expand Down
Loading

0 comments on commit a8dc642

Please sign in to comment.