DefaultAuthenticationProvider.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. using System;
  2. using System.Linq;
  3. using System.Text;
  4. using System.Threading.Tasks;
  5. using MediaBrowser.Common;
  6. using MediaBrowser.Common.Cryptography;
  7. using MediaBrowser.Controller.Authentication;
  8. using MediaBrowser.Controller.Entities;
  9. using MediaBrowser.Model.Cryptography;
  10. namespace Emby.Server.Implementations.Library
  11. {
  12. public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
  13. {
  14. private readonly ICryptoProvider _cryptographyProvider;
  15. public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider)
  16. {
  17. _cryptographyProvider = cryptographyProvider;
  18. }
  19. /// <inheritdoc />
  20. public string Name => "Default";
  21. /// <inheritdoc />
  22. public bool IsEnabled => true;
  23. /// <inheritdoc />
  24. // This is dumb and an artifact of the backwards way auth providers were designed.
  25. // This version of authenticate was never meant to be called, but needs to be here for interface compat
  26. // Only the providers that don't provide local user support use this
  27. public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
  28. {
  29. throw new NotImplementedException();
  30. }
  31. /// <inheritdoc />
  32. // This is the version that we need to use for local users. Because reasons.
  33. public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
  34. {
  35. bool success = false;
  36. if (resolvedUser == null)
  37. {
  38. throw new ArgumentNullException(nameof(resolvedUser));
  39. }
  40. // As long as jellyfin supports passwordless users, we need this little block here to accommodate
  41. if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password))
  42. {
  43. return Task.FromResult(new ProviderAuthenticationResult
  44. {
  45. Username = username
  46. });
  47. }
  48. byte[] passwordbytes = Encoding.UTF8.GetBytes(password);
  49. PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password);
  50. if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id)
  51. || _cryptographyProvider.DefaultHashMethod == readyHash.Id)
  52. {
  53. byte[] calculatedHash = _cryptographyProvider.ComputeHash(
  54. readyHash.Id,
  55. passwordbytes,
  56. readyHash.Salt);
  57. if (calculatedHash.SequenceEqual(readyHash.Hash))
  58. {
  59. success = true;
  60. }
  61. }
  62. else
  63. {
  64. throw new AuthenticationException($"Requested crypto method not available in provider: {readyHash.Id}");
  65. }
  66. if (!success)
  67. {
  68. throw new AuthenticationException("Invalid username or password");
  69. }
  70. return Task.FromResult(new ProviderAuthenticationResult
  71. {
  72. Username = username
  73. });
  74. }
  75. /// <inheritdoc />
  76. public bool HasPassword(User user)
  77. => !string.IsNullOrEmpty(user.Password);
  78. /// <inheritdoc />
  79. public Task ChangePassword(User user, string newPassword)
  80. {
  81. if (string.IsNullOrEmpty(newPassword))
  82. {
  83. user.Password = null;
  84. return Task.CompletedTask;
  85. }
  86. PasswordHash newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword);
  87. user.Password = newPasswordHash.ToString();
  88. return Task.CompletedTask;
  89. }
  90. /// <inheritdoc />
  91. public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
  92. {
  93. if (newPassword != null)
  94. {
  95. newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword).ToString();
  96. }
  97. if (string.IsNullOrWhiteSpace(newPasswordHash))
  98. {
  99. throw new ArgumentNullException(nameof(newPasswordHash));
  100. }
  101. user.EasyPassword = newPasswordHash;
  102. }
  103. /// <inheritdoc />
  104. public string GetEasyPasswordHash(User user)
  105. {
  106. return string.IsNullOrEmpty(user.EasyPassword)
  107. ? null
  108. : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash);
  109. }
  110. /// <summary>
  111. /// Gets the hashed string.
  112. /// </summary>
  113. public string GetHashedString(User user, string str)
  114. {
  115. if (string.IsNullOrEmpty(user.Password))
  116. {
  117. return _cryptographyProvider.CreatePasswordHash(str).ToString();
  118. }
  119. // TODO: make use of iterations parameter?
  120. PasswordHash passwordHash = PasswordHash.Parse(user.Password);
  121. return new PasswordHash(
  122. passwordHash.Id,
  123. _cryptographyProvider.ComputeHash(
  124. passwordHash.Id,
  125. Encoding.UTF8.GetBytes(str),
  126. passwordHash.Salt),
  127. passwordHash.Salt,
  128. passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
  129. }
  130. public byte[] GetHashed(User user, string str)
  131. {
  132. if (string.IsNullOrEmpty(user.Password))
  133. {
  134. return _cryptographyProvider.CreatePasswordHash(str).Hash;
  135. }
  136. // TODO: make use of iterations parameter?
  137. PasswordHash passwordHash = PasswordHash.Parse(user.Password);
  138. return _cryptographyProvider.ComputeHash(
  139. passwordHash.Id,
  140. Encoding.UTF8.GetBytes(str),
  141. passwordHash.Salt);
  142. }
  143. }
  144. }