DefaultPasswordResetProvider.cs 5.0 KB

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