ToString() public method

Gets a canonical string representation for the QueryUri.
public ToString ( ) : string
return string
        /// <summary>
        /// Deletes a `<see cref="Credential"/>` from the storage used by the authentication object.
        /// </summary>
        /// <param name="targetUri">The uniform resource indicator used to uniquely identify the credentials.</param>
        public override void DeleteCredentials(TargetUri targetUri)
        {
            BaseSecureStore.ValidateTargetUri(targetUri);

            Credential credentials = PersonalAccessTokenStore.ReadCredentials(targetUri);

            // This ought to be async, but the base type's method isn't.
            Task.Run(async() =>
            {
                // Attempt to validate the credentials, if they're truly invalid delete them.
                if (!await ValidateCredentials(targetUri, credentials))
                {
                    PersonalAccessTokenStore.DeleteCredentials(targetUri);

                    // Remove any related entries from the tenant cache because tenant change
                    // could the be source of the invalidation, and not purging the cache will
                    // trap the user in a limbo state of invalid credentials.

                    // Deserialize the cache and remove any matching entry.
                    string tenantUrl = targetUri.ToString();
                    var cache        = await DeserializeTenantCache();

                    // Attempt to remove the URL entry, if successful serialize the cache.
                    if (cache.Remove(tenantUrl))
                    {
                        await SerializeTenantCache(cache);
                    }
                }
            }).Wait();
        }
        internal async Task <TargetUri> GetIdentityServiceUri(TargetUri targetUri, Secret authorization)
        {
            const string LocationServiceUrlPathAndQuery = "_apis/ServiceDefinitions/LocationService2/951917AC-A960-4999-8464-E3F0AA25B381?api-version=1.0";

            if (targetUri is null)
            {
                throw new ArgumentNullException(nameof(targetUri));
            }
            if (authorization is null)
            {
                throw new ArgumentNullException(nameof(authorization));
            }

            string tenantUrl = targetUri.ToString(false, true, false);

            if (targetUri.TargetUriContainsUsername)
            {
                string escapedUserInfo = Uri.EscapeUriString(targetUri.TargetUriUsername);
                tenantUrl = tenantUrl + escapedUserInfo + "/";
            }

            var locationServiceUrl = tenantUrl + LocationServiceUrlPathAndQuery;
            var requestUri         = new TargetUri(locationServiceUrl, targetUri.ProxyUri?.ToString());
            var options            = new NetworkRequestOptions(true)
            {
                Authorization = authorization,
            };

            try
            {
                using (var response = await Network.HttpGetAsync(requestUri, options))
                {
                    if (response.IsSuccessStatusCode)
                    {
                        using (HttpContent content = response.Content)
                        {
                            string responseText = await content.ReadAsStringAsync();

                            Match match;
                            if ((match = Regex.Match(responseText, @"\""location\""\:\""([^\""]+)\""", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase)).Success)
                            {
                                string identityServiceUrl  = match.Groups[1].Value;
                                var    idenitityServiceUri = new Uri(identityServiceUrl, UriKind.Absolute);

                                return(new TargetUri(idenitityServiceUri, targetUri.ProxyUri));
                            }
                        }
                    }

                    Trace.WriteLine($"failed to find Identity Service for '{targetUri}' via location service [{(int)response.StatusCode} {response.ReasonPhrase}].");
                }
            }
            catch (Exception exception)
            {
                Trace.WriteException(exception);
                throw new VstsLocationServiceException($"Failed to find Identity Service for `{targetUri}`.", exception);
            }

            return(null);
        }
        private StringContent GetAccessTokenRequestBody(TargetUri targetUri, VstsTokenScope tokenScope, TimeSpan?duration = null)
        {
            const string ContentBasicJsonFormat = "{{ \"scope\" : \"{0}\", \"displayName\" : \"Git: {1} on {2}\" }}";
            const string ContentTimedJsonFormat = "{{ \"scope\" : \"{0}\", \"displayName\" : \"Git: {1} on {2}\", \"validTo\": \"{3:u}\" }}";
            const string HttpJsonContentType    = "application/json";

            if (targetUri is null)
            {
                throw new ArgumentNullException(nameof(targetUri));
            }
            if (tokenScope is null)
            {
                throw new ArgumentNullException(nameof(tokenScope));
            }

            string tokenUrl = targetUri.ToString(username: false,
                                                 port: true,
                                                 path: false);

            if (targetUri.TargetUriContainsUsername)
            {
                string escapedUserInfo = Uri.EscapeUriString(targetUri.TargetUriUsername);
                tokenUrl = tokenUrl + escapedUserInfo + "/";
            }

            Trace.WriteLine($"creating access token scoped to '{tokenScope}' for '{targetUri}'");

            string jsonContent = (duration.HasValue && duration.Value > TimeSpan.FromHours(1))
                ? string.Format(InvariantCulture, ContentTimedJsonFormat, tokenScope, tokenUrl, Environment.MachineName, DateTime.UtcNow + duration.Value)
                : string.Format(InvariantCulture, ContentBasicJsonFormat, tokenScope, tokenUrl, Environment.MachineName);
            StringContent content = new StringContent(jsonContent, Encoding.UTF8, HttpJsonContentType);

            return(content);
        }
Esempio n. 4
0
        /// <summary>
        /// Deletes a `<see cref="Credential"/>` from the storage used by the authentication object.
        /// </summary>
        /// <param name="targetUri">The uniform resource indicator used to uniquely identify the credentials.</param>
        public override async Task <bool> DeleteCredentials(TargetUri targetUri)
        {
            if (targetUri is null)
            {
                throw new ArgumentNullException(nameof(targetUri));
            }

            Credential credentials = await PersonalAccessTokenStore.ReadCredentials(targetUri);

            // Attempt to validate the credentials, if they're truly invalid delete them.
            if (!await ValidateCredentials(targetUri, credentials))
            {
                await PersonalAccessTokenStore.DeleteCredentials(targetUri);

                // Remove any related entries from the tenant cache because tenant change
                // could the be source of the invalidation, and not purging the cache will
                // trap the user in a limbo state of invalid credentials.

                // Deserialize the cache and remove any matching entry.
                string tenantUrl = targetUri.ToString();
                var    cache     = await DeserializeTenantCache(Context);

                // Attempt to remove the URL entry, if successful serialize the cache.
                if (cache.Remove(tenantUrl))
                {
                    await SerializeTenantCache(Context, cache);
                }

                return(true);
            }

            return(false);
        }
        public static string UriToUrl(TargetUri targetUri, string @namespace)
        {
            BaseSecureStore.ValidateTargetUri(targetUri);
            if (string.IsNullOrWhiteSpace(@namespace))
            {
                throw new ArgumentNullException(@namespace);
            }

            string targetName = $"{@namespace}:{targetUri.ToString(false, true, true)}";

            targetName = targetName.TrimEnd('/', '\\');

            return(targetName);
        }
        internal static string GetTargetUrl(TargetUri targetUri)
        {
            string requestUrl = targetUri.ToString(false, true, false);

            // Handle the Azure userinfo -> path conversion.AzureBaseUrlHost
            if (targetUri.Host.EndsWith(AzureBaseUrlHost, StringComparison.OrdinalIgnoreCase) &&
                targetUri.ContainsUserInfo)
            {
                string escapedUserInfo = Uri.EscapeUriString(targetUri.UserInfo);

                requestUrl = requestUrl + escapedUserInfo + "/";
            }

            return(requestUrl);
        }
Esempio n. 7
0
        /// <summary>
        /// Generate a key based on the `<paramref name="targetUri"/>`, may include username, port, etc.
        /// </summary>
        public static string UriToIdentityUrl(TargetUri targetUri, string @namespace)
        {
            if (targetUri is null)
            {
                throw new ArgumentNullException(nameof(targetUri));
            }
            if (string.IsNullOrWhiteSpace(@namespace))
            {
                throw new ArgumentNullException(@namespace);
            }

            string targetName = $"{@namespace}:{targetUri.ToString(true, true, true)}";

            targetName = targetName.TrimEnd('/', '\\');

            return(targetName);
        }
        internal static TargetUri GetConnectionDataUri(TargetUri targetUri)
        {
            const string VstsValidationUrlPath = "_apis/connectiondata";

            if (targetUri is null)
            {
                throw new ArgumentNullException(nameof(targetUri));
            }

            string requestUrl = targetUri.ToString(false, true, false);

            if (targetUri.TargetUriContainsUsername)
            {
                string escapedUserInfo = Uri.EscapeUriString(targetUri.TargetUriUsername);

                requestUrl = requestUrl + escapedUserInfo + "/";
            }

            // Create a URL to the connection data end-point, it's deployment level and "always on".
            string validationUrl = requestUrl + VstsValidationUrlPath;

            return(new TargetUri(validationUrl, targetUri.ProxyUri?.ToString()));
        }
        public static async Task <KeyValuePair <bool, Guid> > DetectAuthority(TargetUri targetUri)
        {
            const string VstsBaseUrlHost          = "visualstudio.com";
            const string VstsResourceTenantHeader = "X-VSS-ResourceTenant";

            BaseSecureStore.ValidateTargetUri(targetUri);

            var tenantId = Guid.Empty;

            if (targetUri.Host.EndsWith(VstsBaseUrlHost, StringComparison.OrdinalIgnoreCase))
            {
                Git.Trace.WriteLine($"'{targetUri}' is subdomain of '{VstsBaseUrlHost}', checking AAD vs MSA.");

                string      tenant = null;
                WebResponse response;

                if (StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, "http") ||
                    StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, "https"))
                {
                    // Query the cache first
                    string tenantUrl = targetUri.ToString();

                    // Read the cache from disk
                    var cache = await DeserializeTenantCache();

                    // Check the cache for an existing value
                    if (cache.TryGetValue(tenantUrl, out tenantId))
                    {
                        return(new KeyValuePair <bool, Guid>(true, tenantId));
                    }

                    try
                    {
                        // Build a request that we expect to fail, do not allow redirect to sign in url
                        var request = WebRequest.CreateHttp(targetUri);
                        request.UserAgent         = Global.UserAgent;
                        request.Method            = "HEAD";
                        request.AllowAutoRedirect = false;
                        // Get the response from the server
                        response = await request.GetResponseAsync();
                    }
                    catch (WebException exception)
                    {
                        Git.Trace.WriteLine($"unable to get response from '{targetUri}' due to '{exception.Status}'.");

                        // Given the number proxy related failures we see, emit a message about the failure potentially
                        // being related to a proxy or gateway misconfiguration.
                        switch (exception.Status)
                        {
                        case WebExceptionStatus.ConnectFailure:
                        case WebExceptionStatus.PipelineFailure:
                        case WebExceptionStatus.ProtocolError:
                        case WebExceptionStatus.ReceiveFailure:
                        case WebExceptionStatus.SecureChannelFailure:
                        case WebExceptionStatus.SendFailure:
                        case WebExceptionStatus.ServerProtocolViolation:
                        case WebExceptionStatus.TrustFailure:
                        {
                            Git.Trace.WriteLine($"is there a proxy or gateway incorrectly configured?");
                        }
                        break;
                        }

                        response = exception.Response;
                    }

                    // If the response exists and we have headers, parse them
                    if (response != null && response.SupportsHeaders)
                    {
                        // Find the VSTS resource tenant entry
                        tenant = response.Headers[VstsResourceTenantHeader];

                        if (!string.IsNullOrWhiteSpace(tenant) &&
                            Guid.TryParse(tenant, out tenantId))
                        {
                            // Update the cache.
                            cache[tenantUrl] = tenantId;

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

                            // Success, notify the caller
                            return(new KeyValuePair <bool, Guid>(true, tenantId));
                        }
                    }
                }
                else
                {
                    Git.Trace.WriteLine($"detected non-https based protocol: {targetUri.Scheme}.");
                }
            }

            // if all else fails, fallback to basic authentication
            return(new KeyValuePair <bool, Guid>(false, tenantId));
        }
Esempio n. 10
0
        public static async Task <KeyValuePair <bool, Guid> > DetectAuthority(TargetUri targetUri)
        {
            const string VstsBaseUrlHost          = "visualstudio.com";
            const string VstsResourceTenantHeader = "X-VSS-ResourceTenant";

            BaseSecureStore.ValidateTargetUri(targetUri);

            var tenantId = Guid.Empty;

            if (targetUri.Host.EndsWith(VstsBaseUrlHost, StringComparison.OrdinalIgnoreCase))
            {
                Git.Trace.WriteLine($"'{targetUri}' is subdomain of '{VstsBaseUrlHost}', checking AAD vs MSA.");

                string      tenant = null;
                WebResponse response;

                if (StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, "http") ||
                    StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, "https"))
                {
                    // Query the cache first
                    string tenantUrl = targetUri.ToString();

                    // Read the cache from disk
                    var cache = await DeserializeTenantCache();

                    // Check the cache for an existing value
                    if (cache.TryGetValue(tenantUrl, out tenantId))
                    {
                        return(new KeyValuePair <bool, Guid>(true, tenantId));
                    }

                    try
                    {
                        // build a request that we expect to fail, do not allow redirect to sign in url
                        var request = WebRequest.CreateHttp(targetUri);
                        request.UserAgent         = Global.UserAgent;
                        request.Method            = "HEAD";
                        request.AllowAutoRedirect = false;
                        // get the response from the server
                        response = await request.GetResponseAsync();
                    }
                    catch (WebException exception)
                    {
                        response = exception.Response;
                    }

                    // if the response exists and we have headers, parse them
                    if (response != null && response.SupportsHeaders)
                    {
                        // find the VSTS resource tenant entry
                        tenant = response.Headers[VstsResourceTenantHeader];

                        if (!String.IsNullOrWhiteSpace(tenant) &&
                            Guid.TryParse(tenant, out tenantId))
                        {
                            // Update the cache.
                            cache[tenantUrl] = tenantId;

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

                            // Success, notify the caller
                            return(new KeyValuePair <bool, Guid>(true, tenantId));
                        }
                    }
                }
                else
                {
                    Git.Trace.WriteLine($"detected non-https based protocol: {targetUri.Scheme}.");
                }
            }

            // if all else fails, fallback to basic authentication
            return(new KeyValuePair <bool, Guid>(false, tenantId));
        }
Esempio n. 11
0
        public static async Task <Guid?> DetectAuthority(RuntimeContext context, TargetUri targetUri)
        {
            const string VstsBaseUrlHost          = "visualstudio.com";
            const string VstsResourceTenantHeader = "X-VSS-ResourceTenant";

            BaseSecureStore.ValidateTargetUri(targetUri);

            var tenantId = Guid.Empty;

            if (targetUri.Host.EndsWith(VstsBaseUrlHost, StringComparison.OrdinalIgnoreCase))
            {
                context.Trace.WriteLine($"'{targetUri}' is subdomain of '{VstsBaseUrlHost}', checking AAD vs MSA.");

                string tenant = null;

                if (StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, Uri.UriSchemeHttp) ||
                    StringComparer.OrdinalIgnoreCase.Equals(targetUri.Scheme, Uri.UriSchemeHttps))
                {
                    // Query the cache first.
                    string tenantUrl = targetUri.ToString();

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

                    // Check the cache for an existing value.
                    if (cache.TryGetValue(tenantUrl, out tenantId))
                    {
                        return(tenantId);
                    }

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

                    try
                    {
                        using (var response = await context.Network.HttpGetAsync(targetUri, options))
                        {
                            if (response.IsSuccessStatusCode)
                            {
                                if (response.Headers.TryGetValues(VstsResourceTenantHeader, out IEnumerable <string> values))
                                {
                                    tenant = System.Linq.Enumerable.First(values);

                                    if (!string.IsNullOrWhiteSpace(tenant) &&
                                        Guid.TryParse(tenant, out tenantId))
                                    {
                                        // Update the cache.
                                        cache[tenantUrl] = tenantId;

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

                                        // Success, notify the caller
                                        return(tenantId);
                                    }
                                }
                            }
                            else
                            {
                                context.Trace.WriteLine($"unable to get response from '{targetUri}', server responded with '{(int)response.StatusCode} {response.StatusCode}'.");
                            }
                        }
                    }
                    catch (HttpRequestException exception)
                    {
                        context.Trace.WriteLine($"unable to get response from '{targetUri}' due to '{exception.Message}'.");
                    }
                }
                else
                {
                    context.Trace.WriteLine($"detected non-https based protocol: '{targetUri.Scheme}'.");
                }
            }

            // Fallback to basic authentication.
            return(null);
        }