private async Task Rollback(CancellationToken cancellationToken, params StoreTypes[] rollback)
        {
            if (rollback == null)
            {
                throw new ArgumentNullException(nameof(rollback));
            }
            foreach (var i in rollback)
            {
                switch (i)
                {
                case StoreTypes.UserStore:
                    await UserStore.RollbackAsync(cancellationToken);

                    break;

                case StoreTypes.EmailStore:
                    await EmailStore.RollbackAsync(cancellationToken);

                    break;

                case StoreTypes.LockoutStore:
                    await LockoutStore.RollbackAsync(cancellationToken);

                    break;

                case StoreTypes.NameStore:
                    await NameStore.RollbackAsync(cancellationToken);

                    break;

                case StoreTypes.PasswordStore:
                    await PasswordStore.RollbackAsync(cancellationToken);

                    break;

                case StoreTypes.TokenStore:
                    await TokenStore.RollbackAsync(cancellationToken);

                    break;

                case StoreTypes.ClaimStore:
                    await ClaimStore.RollbackAsync(cancellationToken);

                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
        }
        public async Task <AuthenticationResult> RemoveUser(TUser user, CancellationToken cancellationToken)
        {
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
            Handle(cancellationToken);
            var stores = new List <StoreTypes>();

            var result = await EmailStore.DeleteUserAsync(user, cancellationToken);

            stores.Add(StoreTypes.EmailStore);
            await AssertSingle(user, result, cancellationToken, stores);

            result = await ClaimStore.DeleteUserAsync(user, cancellationToken);

            stores.Add(StoreTypes.ClaimStore);
            await AssertSingle(user, result, cancellationToken, stores);

            await LoginStore.DeleteUser(user, cancellationToken);

            stores.Add(StoreTypes.LoginStore);

            await TokenStore.RemoveUserAsync(user, cancellationToken);

            stores.Add(StoreTypes.TokenStore);

            result = await LockoutStore.DeleteUserAsync(user, cancellationToken);

            stores.Add(StoreTypes.NameStore);
            await AssertSingle(user, result, cancellationToken, stores);

            result = await NameStore.DeleteUserAsync(user, cancellationToken);

            stores.Add(StoreTypes.NameStore);
            await AssertSingle(user, result, cancellationToken, stores);

            result = await PasswordStore.DeleteUserAsync(user, cancellationToken);

            stores.Add(StoreTypes.PasswordStore);
            await AssertSingle(user, result, cancellationToken, stores);

            result = await UserStore.DeleteUserAsync(user, cancellationToken);

            stores.Add(StoreTypes.UserStore);
            await AssertSingle(user, result, cancellationToken, stores);

            return(AuthenticationResult.Success());
        }
 public void Dispose()
 {
     if (IsDiposed)
     {
         return;
     }
     UserStore.Dispose();
     PasswordStore.Dispose();
     EmailStore.Dispose();
     TokenStore.Dispose();
     LockoutStore.Dispose();
     NameStore.Dispose();
     ClaimStore.Dispose();
     IsDiposed = true;
 }
        public async Task <AuthenticationResult <string> > CreateUserAsync(TUser user, string name, string username,
                                                                           string password, string email, CancellationToken cancellationToken)
        {
            // Handle any cancellation
            Handle(cancellationToken);

            // Quick validation of the parameters supplied. Validation of empty strings should be done above as well.
            if (user == null)
            {
                throw new ArgumentNullException(nameof(user));
            }
            if (string.IsNullOrEmpty(username))
            {
                throw new ArgumentNullException(nameof(username));
            }
            if (string.IsNullOrEmpty(nameof(password)))
            {
                throw new ArgumentNullException(nameof(password));
            }
            if (string.IsNullOrEmpty(nameof(email)))
            {
                throw new ArgumentNullException(nameof(email));
            }

            // Validates the parameters
            var validation = await ValidationConfiguration.EmailValidator.ValidateAsync(email, cancellationToken);

            if (!validation.Succeeded)
            {
                return(AuthenticationResult <string> .Invalidate(validation));
            }
            validation = await ValidationConfiguration.UserNameValidator.ValidateAsync(username, cancellationToken);

            if (!validation.Succeeded)
            {
                return(AuthenticationResult <string> .Invalidate(validation));
            }
            validation = await ValidationConfiguration.NameValidator.ValidateAsync(name, cancellationToken);

            if (!validation.Succeeded)
            {
                return(AuthenticationResult <string> .Invalidate(validation));
            }
            validation = await ValidationConfiguration.PasswordValidator.ValidateAsync(password, cancellationToken);

            if (!validation.Succeeded)
            {
                return(AuthenticationResult <string> .Invalidate(validation));
            }
            validation = await ValidationConfiguration.UserValidator.ValidateAsync(user, cancellationToken);

            if (!validation.Succeeded)
            {
                return(AuthenticationResult <string> .Invalidate(validation));
            }

            // Create the id of the new user
            var id = Guid.NewGuid().ToString();

            // All stores that have been modified
            var a = new List <StoreTypes>();

            // Add user in database and retrieve the resulting user
            var queryResult = await UserStore.CreateUserAsync(user, id, username, DateTime.Now, cancellationToken);

            a.Add(StoreTypes.UserStore);
            if (!queryResult.Succeeded || queryResult.RowsModified != 1)
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }
            var qUser = queryResult.Result;

            // Add user to email store
            var result = await EmailStore.CreateUserAsync(qUser, email, cancellationToken);

            a.Add(StoreTypes.EmailStore);
            if (!result.Succeeded || result.RowsModified != 1)
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }

            //Add user to password store
            var salt = await SecurityConfiguration.RandomProvider.GenerateRandomAsync(cancellationToken);

            var hashed = await SecurityConfiguration.PasswordHasher.HashPassword(password, salt, cancellationToken);

            result = await PasswordStore.CreateUserAsync(qUser, hashed, salt, cancellationToken);

            a.Add(StoreTypes.PasswordStore);
            if (!result.Succeeded || result.RowsModified != 1)
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }

            // Start adding the email and configure it to be activated with a link.
            var guid  = Guid.NewGuid().ToString();
            var token = Convert.ToBase64String(
                SecurityConfiguration.TokenProvider.CreateToken(qUser, guid, Security.TokenField.Activation));

            result = await EmailStore.CreateUserAsync(qUser, email, cancellationToken);

            a.Add(StoreTypes.EmailStore);
            if (!result.Succeeded || result.RowsModified != 1)
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }

            result = await TokenStore.CreateTokenAsync(qUser, token, cancellationToken);

            a.Add(StoreTypes.TokenStore);
            if (!result.Succeeded || result.RowsModified != 1)
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }

            // Email the user with the result
            await EmailConfiguration.AccountVerificationTemplate.LoadAsync(new
            {
                Email = email,
                Token = id
            });

            await EmailConfiguration.EmailProvider.Email(EmailConfiguration.AccountVerificationTemplate, email, cancellationToken);

            // Add a lockout field
            result = await LockoutStore.CreateUserAsync(qUser, cancellationToken);

            a.Add(StoreTypes.LockoutStore);
            if (!result.Succeeded || result.RowsModified != 1)
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }

            // Add a potential claims field
            result = await ClaimStore.CreateClaimsAsync(qUser, SecurityConfiguration.DefaultClaims, cancellationToken);

            a.Add(StoreTypes.ClaimStore);
            if (!result.Succeeded || result.RowsModified != SecurityConfiguration.DefaultClaims.Count())
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }

            result = await NameStore.CreateUserAsync(qUser, name, cancellationToken);

            a.Add(StoreTypes.NameStore);
            if (!result.Succeeded || result.RowsModified != SecurityConfiguration.DefaultClaims.Count())
            {
                await Rollback(cancellationToken, a.ToArray());

                return(AuthenticationResult <string> .ServerFault());
            }

            await Commit(cancellationToken, a.ToArray());

            return(AuthenticationResult <string> .Success(id));
        }