DefaultPasswordResetProvider.cs 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Text;
  7. using System.Threading.Tasks;
  8. using MediaBrowser.Common.Extensions;
  9. using MediaBrowser.Controller.Authentication;
  10. using MediaBrowser.Controller.Configuration;
  11. using MediaBrowser.Controller.Library;
  12. using MediaBrowser.Model.Cryptography;
  13. using MediaBrowser.Model.Serialization;
  14. using MediaBrowser.Model.Users;
  15. namespace Emby.Server.Implementations.Library
  16. {
  17. public class DefaultPasswordResetProvider : IPasswordResetProvider
  18. {
  19. public string Name => "Default Password Reset Provider";
  20. public bool IsEnabled => true;
  21. private readonly string _passwordResetFileBase;
  22. private readonly string _passwordResetFileBaseDir;
  23. private readonly string _passwordResetFileBaseName = "passwordreset";
  24. private readonly IJsonSerializer _jsonSerializer;
  25. private readonly IUserManager _userManager;
  26. private readonly ICryptoProvider _crypto;
  27. public DefaultPasswordResetProvider(IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IUserManager userManager, ICryptoProvider cryptoProvider)
  28. {
  29. _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath;
  30. _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, _passwordResetFileBaseName);
  31. _jsonSerializer = jsonSerializer;
  32. _userManager = userManager;
  33. _crypto = cryptoProvider;
  34. }
  35. public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
  36. {
  37. SerializablePasswordReset spr;
  38. HashSet<string> usersreset = new HashSet<string>();
  39. foreach (var resetfile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{_passwordResetFileBaseName}*"))
  40. {
  41. using (var str = File.OpenRead(resetfile))
  42. {
  43. spr = await _jsonSerializer.DeserializeFromStreamAsync<SerializablePasswordReset>(str).ConfigureAwait(false);
  44. }
  45. if (spr.ExpirationDate < DateTime.Now)
  46. {
  47. File.Delete(resetfile);
  48. }
  49. else if (spr.Pin.Replace("-", "").Equals(pin.Replace("-", ""), StringComparison.InvariantCultureIgnoreCase))
  50. {
  51. var resetUser = _userManager.GetUserByName(spr.UserName);
  52. if (resetUser == null)
  53. {
  54. throw new Exception($"User with a username of {spr.UserName} not found");
  55. }
  56. await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false);
  57. usersreset.Add(resetUser.Name);
  58. File.Delete(resetfile);
  59. }
  60. }
  61. if (usersreset.Count < 1)
  62. {
  63. throw new ResourceNotFoundException($"No Users found with a password reset request matching pin {pin}");
  64. }
  65. else
  66. {
  67. return new PinRedeemResult
  68. {
  69. Success = true,
  70. UsersReset = usersreset.ToArray()
  71. };
  72. }
  73. }
  74. public async Task<ForgotPasswordResult> StartForgotPasswordProcess(MediaBrowser.Controller.Entities.User user, bool isInNetwork)
  75. {
  76. string pin = string.Empty;
  77. using (var cryptoRandom = System.Security.Cryptography.RandomNumberGenerator.Create())
  78. {
  79. byte[] bytes = new byte[4];
  80. cryptoRandom.GetBytes(bytes);
  81. pin = BitConverter.ToString(bytes);
  82. }
  83. DateTime expireTime = DateTime.Now.AddMinutes(30);
  84. string filePath = _passwordResetFileBase + user.InternalId + ".json";
  85. SerializablePasswordReset spr = new SerializablePasswordReset
  86. {
  87. ExpirationDate = expireTime,
  88. Pin = pin,
  89. PinFile = filePath,
  90. UserName = user.Name
  91. };
  92. try
  93. {
  94. using (FileStream fileStream = File.OpenWrite(filePath))
  95. {
  96. _jsonSerializer.SerializeToStream(spr, fileStream);
  97. await fileStream.FlushAsync().ConfigureAwait(false);
  98. }
  99. }
  100. catch (Exception e)
  101. {
  102. throw new Exception($"Error serializing or writing password reset for {user.Name} to location: {filePath}", e);
  103. }
  104. return new ForgotPasswordResult
  105. {
  106. Action = ForgotPasswordAction.PinCode,
  107. PinExpirationDate = expireTime,
  108. PinFile = filePath
  109. };
  110. }
  111. private class SerializablePasswordReset : PasswordPinCreationResult
  112. {
  113. public string Pin { get; set; }
  114. public string UserName { get; set; }
  115. }
  116. }
  117. }