コード例 #1
0
        /// <inheritdoc/>
        public override Credential GetCredentials(TargetUri targetUri)
        {
            BaseSecureStore.ValidateTargetUri(targetUri);
            Credential credentials = null;

            if ((credentials = PersonalAccessTokenStore.ReadCredentials(targetUri)) != null)
            {
                Trace.WriteLine("successfully retrieved stored credentials, updating credential cache");
                return(credentials);
            }

            // try for a refresh token
            var refreshCredentials = PersonalAccessTokenStore.ReadCredentials(GetRefreshTokenTargetUri(targetUri));

            if (refreshCredentials == null)
            {
                // no refresh token return null
                return(credentials);
            }

            Credential refreshedCredentials = Task.Run(() => RefreshCredentials(targetUri, refreshCredentials.Password, null)).Result;

            if (refreshedCredentials == null)
            {
                // refresh failed return null
                return(credentials);
            }
            else
            {
                credentials = refreshedCredentials;
            }

            return(credentials);
        }
コード例 #2
0
        /// <inheritdoc/>
        public override void DeleteCredentials(TargetUri targetUri, string username)
        {
            BaseSecureStore.ValidateTargetUri(targetUri);

            Trace.WriteLine("BitbucketAuthentication::DeleteCredentials");

            Credential credentials = null;

            if ((credentials = PersonalAccessTokenStore.ReadCredentials(targetUri)) != null)
            {
                // try to delete the credentials for the explicit target uri first
                PersonalAccessTokenStore.DeleteCredentials(targetUri);
                Trace.WriteLine($"host credentials deleted for {targetUri.ActualUri}");
            }

            // tidy up and refresh tokens
            var refreshTargetUri = GetRefreshTokenTargetUri(targetUri);

            if ((credentials = PersonalAccessTokenStore.ReadCredentials(refreshTargetUri)) != null)
            {
                // try to delete the credentials for the explicit target uri first
                PersonalAccessTokenStore.DeleteCredentials(refreshTargetUri);
                Trace.WriteLine($"host refresh credentials deleted for {refreshTargetUri.ActualUri}");
            }
        }
コード例 #3
0
        private async Task <AuthenticationResult> MakeOAuthAdminRequest(HttpClient httpClient, string url, MultipartFormDataContent content)
        {
            using (HttpResponseMessage response = await httpClient.PostAsync(url, content))
            {
                Trace.WriteLine($"server responded with {response.StatusCode}.");

                switch (response.StatusCode)
                {
                case HttpStatusCode.OK:
                case HttpStatusCode.Created:
                {
                    // the request was successful, look for the tokens in the response
                    string responseText = await response.Content.ReadAsStringAsync();

                    var token        = FindAccessToken(responseText);
                    var refreshToken = FindRefreshToken(responseText);
                    return(GetAuthenticationResult(token, refreshToken));
                }

                case HttpStatusCode.Unauthorized:
                {
                    // do something
                    return(new AuthenticationResult(AuthenticationResultType.Failure));
                }

                default:
                    Trace.WriteLine("authentication failed");
                    var error = response.Content.ReadAsStringAsync();
                    return(new AuthenticationResult(AuthenticationResultType.Failure));
                }
            }
        }
コード例 #4
0
        /// <summary>
        /// Use locally stored refresh_token to attempt to retrieve a new access_token.
        /// </summary>
        /// <param name="targetUri"></param>
        /// <param name="refreshToken"></param>
        /// <param name="username"></param>
        /// <returns>
        /// A <see cref="Credential"/> containing the new access_token if successful, null otherwise
        /// </returns>
        private async Task <Credential> RefreshCredentials(TargetUri targetUri, string refreshToken, string username)
        {
            Credential           credentials = null;
            AuthenticationResult result;

            if ((result = await BitbucketAuthority.RefreshToken(targetUri, refreshToken)) == true)
            {
                Trace.WriteLine("token refresh succeeded");

                var tempCredentials = GenerateCredentials(targetUri, username, ref result);
                if (!await BitbucketAuthority.ValidateCredentials(targetUri, username, tempCredentials))
                {
                    // oddly our new access_token failed to work, maybe we've been revoked in the
                    // last millisecond?
                    return(credentials);
                }

                // the new access_token is good, so store it and store the refresh_token used to get it.
                SetCredentials(targetUri, tempCredentials, null);
                var newRefreshCredentials = GenerateRefreshCredentials(targetUri, username, ref result);
                SetCredentials(GetRefreshTokenTargetUri(targetUri), newRefreshCredentials, username);

                credentials = tempCredentials;
            }

            return(credentials);
        }
コード例 #5
0
        /// <inheritdoc/>
        public async Task <AuthenticationResult> AcquireToken(TargetUri targetUri, string username, string password, AuthenticationResultType resultType, TokenScope scope)
        {
            if (resultType == AuthenticationResultType.TwoFactor)
            {
                // a previous attempt to aquire a token failed in a way that suggests the user has
                // Bitbucket 2FA turned on. so attempt to run the OAuth dance...
                OAuth.OAuthAuthenticator oauth = new OAuth.OAuthAuthenticator();
                try
                {
                    var result = await oauth.GetAuthAsync(targetUri, scope, CancellationToken.None);

                    if (!result.IsSuccess)
                    {
                        Trace.WriteLine($"oauth authentication failed");
                        return(new AuthenticationResult(AuthenticationResultType.Failure));
                    }

                    // we got a toke but lets check to see the usernames match
                    var restRootUri = new Uri(_restRootUrl);
                    var authHeader  = GetBearerHeaderAuthHeader(result.Token.Value);
                    var userResult  = await RestClient.TryGetUser(targetUri, RequestTimeout, restRootUri, authHeader);

                    if (!userResult.IsSuccess)
                    {
                        Trace.WriteLine($"oauth user check failed");
                        return(new AuthenticationResult(AuthenticationResultType.Failure));
                    }

                    if (!string.IsNullOrWhiteSpace(userResult.RemoteUsername) && !username.Equals(userResult.RemoteUsername))
                    {
                        Trace.WriteLine($"Remote username [{userResult.RemoteUsername}] != [{username}] supplied username");
                        // make sure the 'real' username is returned
                        return(new AuthenticationResult(AuthenticationResultType.Success, result.Token, result.RefreshToken, userResult.RemoteUsername));
                    }

                    // everything is hunky dory
                    return(result);
                }
                catch (Exception ex)
                {
                    Trace.WriteLine($"oauth authentication failed [{ex.Message}]");
                    return(new AuthenticationResult(AuthenticationResultType.Failure));
                }
            }
            else
            {
                BasicAuthAuthenticator basicauth = new BasicAuthAuthenticator();
                try
                {
                    var restRootUri = new Uri(_restRootUrl);
                    return(await basicauth.GetAuthAsync(targetUri, scope, RequestTimeout, restRootUri, username, password));
                }
                catch (Exception ex)
                {
                    Trace.WriteLine($"basic auth authentication failed [{ex.Message}]");
                    return(new AuthenticationResult(AuthenticationResultType.Failure));
                }
            }
        }
        /// <summary>
        /// Opens a modal UI prompting the user to run the OAuth dance.
        /// </summary>
        /// <param name="title"></param>
        /// <param name="targetUri">contains the URL etc of the Authority</param>
        /// <param name="resultType"></param>
        /// <param name="username"></param>
        /// <returns>
        /// returns true if the user successfully completes the OAuth dance and the returned
        /// access_token is validated, false otherwise
        /// </returns>
        public static bool AuthenticationOAuthModalPrompt(string title, TargetUri targetUri, AuthenticationResultType resultType, string username)
        {
            var oauthViewModel = new OAuthViewModel(resultType == AuthenticationResultType.TwoFactor);

            Trace.WriteLine("prompting user for authentication code.");

            bool useOAuth = ShowViewModel(oauthViewModel, () => new OAuthWindow());

            return(useOAuth);
        }
コード例 #7
0
        /// <inheritdoc/>
        public async Task <Credential> InteractiveLogon(TargetUri targetUri)
        {
            Credential credentials = null;
            string     username;
            string     password;

            // Ask the user for Basic Auth credentials
            if (AcquireCredentialsCallback("Please enter your Bitbucket credentials for ", targetUri, out username, out password))
            {
                AuthenticationResult result;

                if (result = await BitbucketAuthority.AcquireToken(targetUri, username, password, AuthenticationResultType.None, TokenScope))
                {
                    Trace.WriteLine("token acquisition succeeded");

                    credentials = GenerateCredentials(targetUri, username, ref result);
                    SetCredentials(targetUri, credentials, username);

                    // if a result callback was registered, call it
                    if (AuthenticationResultCallback != null)
                    {
                        AuthenticationResultCallback(targetUri, result);
                    }

                    return(credentials);
                }
                else if (result == AuthenticationResultType.TwoFactor)
                {
                    // Basic Auth attempt returned a result indicating the user has 2FA on so prompt
                    // the user to run the OAuth dance.
                    if (AcquireAuthenticationOAuthCallback("", targetUri, result, username))
                    {
                        if (result = await BitbucketAuthority.AcquireToken(targetUri, username, password, AuthenticationResultType.TwoFactor, TokenScope))
                        {
                            Trace.WriteLine("token acquisition succeeded");

                            credentials = GenerateCredentials(targetUri, username, ref result);
                            SetCredentials(targetUri, credentials, username);
                            SetCredentials(GetRefreshTokenTargetUri(targetUri), new Credential(result.RefreshToken.Type.ToString(), result.RefreshToken.Value), username);

                            // if a result callback was registered, call it
                            if (AuthenticationResultCallback != null)
                            {
                                AuthenticationResultCallback(targetUri, result);
                            }

                            return(credentials);
                        }
                    }
                }
            }

            Trace.WriteLine("interactive logon failed");
            return(credentials);
        }
        /// <summary>
        /// Opens a Modal UI prompting the user for Basic Auth credentials.
        /// </summary>
        /// <param name="title"></param>
        /// <param name="targetUri">contains the URL etc of the Authority</param>
        /// <param name="username">the username entered by the user</param>
        /// <param name="password">the password entered by the user</param>
        /// <returns>
        /// returns true if the user provides credentials which are then successfully validated,
        /// false otherwise
        /// </returns>
        public static bool CredentialModalPrompt(string title, TargetUri targetUri, out string username, out string password)
        {
            // if there is a user in the remote URL then prepopulate the UI with it.
            var credentialViewModel = new CredentialsViewModel(GetUserFromTargetUri(targetUri));

            Trace.WriteLine("prompting user for credentials.");

            bool credentialValid = ShowViewModel(credentialViewModel, () => new CredentialsWindow());

            username = credentialViewModel.Login;
            password = credentialViewModel.Password;

            return(credentialValid);
        }
コード例 #9
0
 /// <inheritdoc/>
 public async Task <AuthenticationResult> RefreshToken(TargetUri targetUri, string refreshToken)
 {
     // Refreshing is only an OAuth concept so use the OAuth tools
     OAuth.OAuthAuthenticator oauth = new OAuth.OAuthAuthenticator();
     try
     {
         return(await oauth.RefreshAuthAsync(targetUri, refreshToken, CancellationToken.None));
     }
     catch (Exception ex)
     {
         Trace.WriteLine($"oauth refresh failed [{ex.Message}]");
         return(new AuthenticationResult(AuthenticationResultType.Failure));
     }
 }
コード例 #10
0
        /// <summary>
        /// Validate the provided credentials, made up of the username and the contents if the
        /// authHeader, by making a request to a known Bitbucket REST API resource. A 200/Success
        /// response indicates the credentials are valid. Any other response indicates they are not.
        /// </summary>
        /// <param name="targetUri">
        /// Contains the <see cref="HttpClientHandler"/> used when making the REST API request
        /// </param>
        /// <param name="username">the username to validate</param>
        /// <param name="authHeader">
        /// the HTTP auth header containing the password/access_token to validate
        /// </param>
        /// <returns>true if the credentials are valid, false otherwise.</returns>
        private async Task <bool> ValidateCredentials(TargetUri targetUri, string username, string authHeader)
        {
            const string ValidationUrl = "https://api.bitbucket.org/2.0/user";

            BaseSecureStore.ValidateTargetUri(targetUri);

            Trace.WriteLine($"Auth Type = {authHeader.Substring(0, 5)}");

            // craft the request header for the Bitbucket v2 API w/ credentials
            using (HttpClientHandler handler = targetUri.HttpClientHandler)
                using (HttpClient httpClient = new HttpClient(handler)
                {
                    Timeout = TimeSpan.FromMilliseconds(RequestTimeout)
                })
                {
                    httpClient.DefaultRequestHeaders.Add("User-Agent", Global.UserAgent);
                    httpClient.DefaultRequestHeaders.Add("Authorization", authHeader);

                    using (HttpResponseMessage response = await httpClient.GetAsync(ValidationUrl))
                    {
                        switch (response.StatusCode)
                        {
                        case HttpStatusCode.OK:
                        case HttpStatusCode.Created:
                        {
                            Trace.WriteLine("credential validation succeeded");
                            return(true);
                        }

                        case HttpStatusCode.Forbidden:
                        {
                            Trace.WriteLine("credential validation failed: Forbidden");
                            return(false);
                        }

                        case HttpStatusCode.Unauthorized:
                        {
                            Trace.WriteLine("credential validation failed: Unauthorized");
                            return(false);
                        }

                        default:
                        {
                            Trace.WriteLine("credential validation failed");
                            return(false);
                        }
                        }
                    }
                }
        }
コード例 #11
0
 private AuthenticationResult GetAuthenticationResult(Token token, Token refreshToken)
 {
     // Bitbucket should always return both
     if (token == null || refreshToken == null)
     {
         Trace.WriteLine("authentication failure");
         return(new AuthenticationResult(AuthenticationResultType.Failure));
     }
     else
     {
         Trace.WriteLine("authentication success: new personal access token created.");
         return(new AuthenticationResult(AuthenticationResultType.Success, token, refreshToken));
     }
 }
コード例 #12
0
        /// <inheritdoc/>
        public void SetCredentials(TargetUri targetUri, Credential credentials, string username)
        {
            BaseSecureStore.ValidateTargetUri(targetUri);
            BaseSecureStore.ValidateCredential(credentials);

            Trace.WriteLine($"{credentials.Username} at {targetUri.ActualUri.AbsoluteUri}");

            // if the url doesn't contain a username then save with an explicit username.
            if (!TargetUriContainsUsername(targetUri) && !string.IsNullOrWhiteSpace(username))
            {
                Credential tempCredentials = new Credential(username, credentials.Password);
                SetCredentials(GetPerUserTargetUri(targetUri, username), tempCredentials, null);
            }

            PersonalAccessTokenStore.WriteCredentials(targetUri, credentials);
        }
コード例 #13
0
        /// <summary>
        /// Validate the provided credentials, made up of the username and the contents if the
        /// authHeader, by making a request to a known Bitbucket REST API resource. A 200/Success
        /// response indicates the credentials are valid. Any other response indicates they are not.
        /// </summary>
        /// <param name="targetUri">
        /// Contains the <see cref="HttpClientHandler"/> used when making the REST API request
        /// </param>
        /// <param name="authHeader">
        /// the HTTP auth header containing the password/access_token to validate
        /// </param>
        /// <returns>true if the credentials are valid, false otherwise.</returns>
        private async Task <bool> ValidateCredentials(TargetUri targetUri, string authHeader)
        {
            BaseSecureStore.ValidateTargetUri(targetUri);

            Trace.WriteLine($"Auth Type = {authHeader.Substring(0, 5)}");

            var restRootUrl = new Uri(_restRootUrl);
            var result      = await RestClient.TryGetUser(targetUri, RequestTimeout, restRootUrl, authHeader);

            if (result.Type.Equals(AuthenticationResultType.Success))
            {
                Trace.WriteLine("credential validation succeeded");
                return(true);
            }

            Trace.WriteLine("credential validation failed");
            return(false);
        }
コード例 #14
0
        /// <inheritdoc/>
        public override void DeleteCredentials(TargetUri targetUri, string username)
        {
            BaseSecureStore.ValidateTargetUri(targetUri);

            Trace.WriteLine($"Deleting Bitbucket Credentials for {targetUri.ActualUri}");

            Credential credentials = null;

            if ((credentials = PersonalAccessTokenStore.ReadCredentials(targetUri)) != null)
            {
                // try to delete the credentials for the explicit target uri first
                PersonalAccessTokenStore.DeleteCredentials(targetUri);
                Trace.WriteLine($"host credentials deleted for {targetUri.ActualUri}");
            }

            // tidy up and delete any related refresh tokens
            var refreshTargetUri = GetRefreshTokenTargetUri(targetUri);

            if ((credentials = PersonalAccessTokenStore.ReadCredentials(refreshTargetUri)) != null)
            {
                // try to delete the credentials for the explicit target uri first
                PersonalAccessTokenStore.DeleteCredentials(refreshTargetUri);
                Trace.WriteLine($"host refresh credentials deleted for {refreshTargetUri.ActualUri}");
            }

            // if we deleted per user then we shoudl try and delete the host level credentials too if
            // they match the username
            if (targetUri.TargetUriContainsUsername)
            {
                var hostTargetUri   = targetUri.GetHostTargetUri();
                var hostCredentials = GetCredentials(hostTargetUri);
                var encodedUsername = Uri.EscapeDataString(targetUri.TargetUriUsername);
                if (encodedUsername != username)
                {
                    Trace.WriteLine($"username {username} != targetUri userInfo {encodedUsername}");
                }

                if (hostCredentials != null && hostCredentials.Username.Equals(encodedUsername))
                {
                    DeleteCredentials(targetUri.GetHostTargetUri(), username);
                }
            }
        }
コード例 #15
0
        /// <inheritdoc/>
        public override void SetCredentials(TargetUri targetUri, Credential credentials)
        {
            // this is only called from the store() method so only applies to default host entries
            // calling this from elsewhere may have unintended consequences, use
            // SetCredentials(targetUri, credentials, username) instead

            // only store the credentials as received if they match the uri and user of the existing
            // default entry
            var currentCredentials = GetCredentials(targetUri);

            if (currentCredentials != null && currentCredentials.Username != null && !currentCredentials.Username.Equals(credentials.Username))
            {
                // do nothing as the default is for another username and we don't want to overwrite it
                Trace.WriteLine($"skipping for {targetUri.ActualUri} new username {currentCredentials.Username} != {credentials.Username}");
                return;
            }

            SetCredentials(targetUri, credentials, null);

            // Store() will not call with a username url
            if (targetUri.TargetUriContainsUsername)
            {
                return;
            }

            // see if there is a matching personal refresh token
            var username = credentials.Username;
            var userSpecificTargetUri = targetUri.GetPerUserTargetUri(username);
            var userCredentials       = GetCredentials(userSpecificTargetUri, username);

            if (userCredentials != null && userCredentials.Password.Equals(credentials.Password))
            {
                var userRefreshCredentials = GetCredentials(GetRefreshTokenTargetUri(userSpecificTargetUri), username);
                if (userRefreshCredentials != null)
                {
                    Trace.WriteLine("OAuth RefreshToken");
                    var hostRefreshCredentials = new Credential(credentials.Username, userRefreshCredentials.Password);
                    SetCredentials(GetRefreshTokenTargetUri(targetUri), hostRefreshCredentials, null);
                }
            }
        }
コード例 #16
0
        /// <summary>
        /// Identify the Hosting service from the the targetUri.
        /// </summary>
        /// <param name="targetUri"></param>
        /// <returns>
        /// A <see cref="BaseAuthentication"/> instance if the targetUri represents Bitbucket, null otherwise.
        /// </returns>
        public static BaseAuthentication GetAuthentication(TargetUri targetUri, ICredentialStore personalAccessTokenStore, AcquireCredentialsDelegate acquireCredentialsCallback, AcquireAuthenticationOAuthDelegate acquireAuthenticationOAuthCallback)
        {
            BaseAuthentication authentication = null;

            BaseSecureStore.ValidateTargetUri(targetUri);

            if (personalAccessTokenStore == null)
            {
                throw new ArgumentNullException(nameof(personalAccessTokenStore), $"The `{nameof(personalAccessTokenStore)}` is null or invalid.");
            }

            if (targetUri.ActualUri.DnsSafeHost.EndsWith(BitbucketBaseUrlHost, StringComparison.OrdinalIgnoreCase))
            {
                authentication = new Authentication(personalAccessTokenStore, acquireCredentialsCallback, acquireAuthenticationOAuthCallback);
                Trace.WriteLine("authentication for Bitbucket created");
            }
            else
            {
                authentication = null;
            }

            return(authentication);
        }
コード例 #17
0
        /// <inheritdoc/>
        public async Task <AuthenticationResult> AcquireToken(TargetUri targetUri, string username, string password, AuthenticationResultType resultType, TokenScope scope)
        {
            if (resultType == AuthenticationResultType.TwoFactor)
            {
                // a previous attempt to aquire a token failed in a way that suggests the user has
                // Bitbucket 2FA turned on. so attempt to run the OAuth dance...
                OAuth.OAuthAuthenticator oauth = new OAuth.OAuthAuthenticator();
                try
                {
                    return(await oauth.GetAuthAsync(targetUri, scope, CancellationToken.None));
                }
                catch (Exception ex)
                {
                    Trace.WriteLine($"oauth authentication failed [{ex.Message}]");
                    return(new AuthenticationResult(AuthenticationResultType.Failure));
                }
            }
            else
            {
                // use the provided username and password and attempt a Basic Auth request to a known
                // REST API resource.
                Token token = null;
                using (HttpClientHandler handler = targetUri.HttpClientHandler)
                {
                    using (HttpClient httpClient = new HttpClient(handler)
                    {
                        Timeout = TimeSpan.FromMilliseconds(RequestTimeout)
                    })
                    {
                        string basicAuthValue = String.Format("{0}:{1}", username, password);
                        byte[] authBytes      = Encoding.UTF8.GetBytes(basicAuthValue);
                        basicAuthValue = Convert.ToBase64String(authBytes);
                        httpClient.DefaultRequestHeaders.Add("Authorization", "Basic " + basicAuthValue);

                        var url = new Uri(new Uri(_restRootUrl), UserUrl).AbsoluteUri;
                        using (HttpResponseMessage response = await httpClient.GetAsync(url))
                        {
                            Trace.WriteLine($"server responded with {response.StatusCode}.");

                            switch (response.StatusCode)
                            {
                            case HttpStatusCode.OK:
                            case HttpStatusCode.Created:
                            {
                                // Success with username/passord indicates 2FA is not on so
                                // the 'token' is actually the password if we had a
                                // successful call then the password is good.
                                token = new Token(password, TokenType.Personal);

                                Trace.WriteLine("authentication success: new password token created.");
                                return(new AuthenticationResult(AuthenticationResultType.Success, token));
                            }

                            case HttpStatusCode.Forbidden:
                            {
                                // A 403/Forbidden response indicates the username/password
                                // are recognized and good but 2FA is on in which case we
                                // want to indicate that with the TwoFactor result
                                Trace.WriteLine("two-factor app authentication code required");
                                return(new AuthenticationResult(AuthenticationResultType.TwoFactor));
                            }

                            case HttpStatusCode.Unauthorized:
                            {
                                // username or password are wrong.
                                Trace.WriteLine("authentication failed");
                                return(new AuthenticationResult(AuthenticationResultType.Failure));
                            }

                            default:
                                // any unexpected result can be treated as a failure.
                                Trace.WriteLine("authentication failed");
                                return(new AuthenticationResult(AuthenticationResultType.Failure));
                            }
                        }
                    }
                }
            }
        }