web-dev-qa-db-fra.com

Mot de passe de changement d'identité ASP.NET

J'ai besoin de pouvoir changer le mot de passe pour l'utilisateur par l'administrateur. Ainsi, l'administrateur ne devrait pas entrer le mot de passe actuel de l'utilisateur, il devrait pouvoir définir un nouveau mot de passe. Je regarde la méthode ChangePasswordAsync, mais cette méthode nécessite la saisie d'un ancien mot de passe. Donc, cette méthode n'est pas appropriée pour cette tâche. C'est pourquoi je l'ai fait de la manière suivante:

    [HttpPost]
    public async Task<ActionResult> ChangePassword(ViewModels.Admin.ChangePasswordViewModel model)
    {
        var userManager = HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
        var result = await userManager.RemovePasswordAsync(model.UserId);
        if (result.Succeeded)
        {
            result = await userManager.AddPasswordAsync(model.UserId, model.Password);
            if (result.Succeeded)
            {
                return RedirectToAction("UserList");
            }
            else
            {
                ModelState.AddModelError("", result.Errors.FirstOrDefault());
            }
        }
        else
        {
            ModelState.AddModelError("", result.Errors.FirstOrDefault());
        }
        return View(model);
    }

cela fonctionne, mais théoriquement, nous pouvons recevoir une erreur sur la méthode AddPasswordAsync. Ainsi, l'ancien mot de passe sera supprimé, mais le nouveau n'est pas défini. Ce n'est pas bon. Une façon de le faire en "une transaction"? PS. J'ai vu la méthode ResetPasswordAsync avec un jeton de réinitialisation, semble-t-il, c'est plus sûr (car cela ne peut pas être une situation instable avec l'utilisateur), mais dans tous les cas, il y a deux actions. 

37
Oleg Sh

ApplicationUserManager est la classe générée par le modèle ASP.NET. 

Ce qui signifie que vous pouvez l'éditer et ajouter toutes les fonctionnalités dont il ne dispose pas encore. La classe UserManager a une propriété protégée nommée Store qui stocke une référence à la classe UserStore (ou l'une de ses sous-classes, selon la configuration de votre identité ASP.NET ou si vous utilisez des implémentations de magasin d'utilisateurs personnalisées, c'est-à-dire si vous utilisez un moteur de base de données différent. comme MySQL). 

public class AplicationUserManager : UserManager<....> 
{
    public async Task<IdentityResult> ChangePasswordAsync(TKey userId, string newPassword) 
    {
        var store = this.Store as IUserPasswordStore;
        if(store==null) 
        {
            var errors = new string[] 
            { 
                "Current UserStore doesn't implement IUserPasswordStore"
            };

            return Task.FromResult<IdentityResult>(new IdentityResult(errors) { Succeeded = false });
        }

        if(PasswordValidator != null)
        {
            var passwordResult = await PasswordValidator.ValidateAsync(password);
            if(!password.Result.Success)
                return passwordResult;
        }

        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);

        await store.SetPasswordHashAsync(userId, newPasswordHash);
        return Task.FromResult<IdentityResult>(IdentityResult.Success);
    }
}

La UserManager n’est rien d’autre qu’un wrapper pour la UserStore sous-jacente. Consultez la documentation de l'interface IUserPasswordStore à MSDN sur les méthodes disponibles. 

Edit: La PasswordHasher est également une propriété publique de la classe UserManager, voir définition de l'interface ici .

Edit 2: Certaines personnes naïvement pensent que vous ne pouvez pas valider votre mot de passe de cette façon, je l'ai mis à jour. La propriété PasswordHasher est également une propriété de UserManager et elle est aussi simple que d'ajouter 2 lignes de code pour ajouter également la validation du mot de passe (ce qui n'était cependant pas une exigence de la question d'origine).

26
Tseng

Cette méthode a fonctionné pour moi:

public async Task<IHttpActionResult> changePassword(UsercredentialsModel usermodel)
{
  ApplicationUser user = await AppUserManager.FindByIdAsync(usermodel.Id);
  if (user == null)
  {
    return NotFound();
  }
  user.PasswordHash = AppUserManager.PasswordHasher.HashPassword(usermodel.Password);
  var result = await AppUserManager.UpdateAsync(user);
  if (!result.Succeeded)
  {
    //throw exception......
  }
  return Ok();
}
43
bryan c

EDIT: Je sais que le PO a demandé une réponse qui effectue la tâche en une transaction, mais je pense que le code est utile aux gens.

Toutes les réponses utilisent directement PasswordHasher, ce qui n’est pas une bonne idée car vous perdrez certaines fonctionnalités (validation, etc.).

Une alternative (et je suppose que l'approche recommandée) est de créer un jeton de réinitialisation de mot de passe, puis de l'utiliser pour changer le mot de passe. Exemple:

var user = await UserManager.FindByIdAsync(id);

var token = await UserManager.GeneratePasswordResetTokenAsync(user);

var result = await UserManager.ResetPasswordAsync(user, token, "MyN3wP@ssw0rd");
33
Lee Gunn

Ceci est juste un raffinement de la réponse fournie par @Tseng. (J'ai dû le modifier pour le faire fonctionner). 

public class AppUserManager : UserManager<AppUser, int>
{
    .
    // standard methods...
    .

    public async Task<IdentityResult> ChangePasswordAsync(AppUser user, string newPassword)
    {
        if (user == null)
            throw new ArgumentNullException(nameof(user));

        var store = this.Store as IUserPasswordStore<AppUser, int>;
        if (store == null)
        {
            var errors = new string[] { "Current UserStore doesn't implement IUserPasswordStore" };
            return IdentityResult.Failed(errors);
        }

        var newPasswordHash = this.PasswordHasher.HashPassword(newPassword);
        await store.SetPasswordHashAsync(user, newPasswordHash);
        await store.UpdateAsync(user);
        return IdentityResult.Success;
    }
}

Remarque: cela s'applique spécifiquement à une configuration modifiée qui utilise int comme clé primaire pour les utilisateurs et les rôles. Je pense qu'il suffirait simplement de supprimer les arguments de type <AppUser, int> pour le faire fonctionner avec la configuration d'identité ASP.NET par défaut. 

5
BCA
public async Task<IActionResult> ChangePassword(ChangePwdViewModel usermodel)
        {           
            var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
            var user = await _userManager.FindByIdAsync(userId);            
            var result = await _userManager.ChangePasswordAsync(user, usermodel.oldPassword, usermodel.newPassword);
            if (!result.Succeeded)
            {
                //throw exception......
            }
            return Ok();
        }

public class ChangePwdViewModel
    {  
        [DataType(DataType.Password), Required(ErrorMessage ="Old Password Required")]
        public string oldPassword { get; set; }

        [DataType(DataType.Password), Required(ErrorMessage ="New Password Required")]
        public string newPassword { get; set; }
    }

Remarque: ici UserId je récupère de l'utilisateur actuellement connecté.

2
Mansur Haider
public async Task<ActionResult> ChangePassword(ResetPasswordViewModel CP)
{
     ApplicationDbContext context = new ApplicationDbContext();
     UserStore<ApplicationUser> store = new UserStore<ApplicationUser>(context);
     UserManager<ApplicationUser> UserManager = new UserManager<ApplicationUser>(store);
     var user = await UserManager.FindAsync(User.Identity.Name, CP.CurrentPassword);

     if (!UserManager.CheckPassword(user, CP.CurrentPassword))
     {
           ViewBag.notification = "Incorrect password.";
           return View("~/Views/User/settings.cshtml");
     }
     else
     {
           if (CP.Password != CP.ConfirmPassword)
           {
                 ViewBag.notification = "try again";
                 return View("~/Views/User/settings.cshtml");
           }
           else
           {
                 String hashedNewPassword = UserManager.PasswordHasher.HashPassword(CP.Password);
                 await store.SetPasswordHashAsync(user, hashedNewPassword);
                 await store.UpdateAsync(user);
                 ViewBag.notification = "successful";
                 return View("~/Views/User/settings.cshtml");
            }
      }
 }
0
Majid joghataey