DefaultAuthenticationProvider.cs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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(readyHash.Id, passwordbytes, readyHash.Salt);
  54. if (calculatedHash.SequenceEqual(readyHash.Hash))
  55. {
  56. success = true;
  57. }
  58. }
  59. else
  60. {
  61. throw new AuthenticationException($"Requested crypto method not available in provider: {readyHash.Id}");
  62. }
  63. if (!success)
  64. {
  65. throw new AuthenticationException("Invalid username or password");
  66. }
  67. return Task.FromResult(new ProviderAuthenticationResult
  68. {
  69. Username = username
  70. });
  71. }
  72. /// <inheritdoc />
  73. public bool HasPassword(User user)
  74. => !string.IsNullOrEmpty(user.Password);
  75. /// <inheritdoc />
  76. public Task ChangePassword(User user, string newPassword)
  77. {
  78. if (string.IsNullOrEmpty(newPassword))
  79. {
  80. user.Password = null;
  81. return Task.CompletedTask;
  82. }
  83. PasswordHash newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword);
  84. user.Password = newPasswordHash.ToString();
  85. return Task.CompletedTask;
  86. }
  87. /// <inheritdoc />
  88. public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash)
  89. {
  90. if (newPassword != null)
  91. {
  92. newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword).ToString();
  93. }
  94. if (string.IsNullOrWhiteSpace(newPasswordHash))
  95. {
  96. throw new ArgumentNullException(nameof(newPasswordHash));
  97. }
  98. user.EasyPassword = newPasswordHash;
  99. }
  100. /// <inheritdoc />
  101. public string GetEasyPasswordHash(User user)
  102. {
  103. return string.IsNullOrEmpty(user.EasyPassword)
  104. ? null
  105. : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash);
  106. }
  107. /// <summary>
  108. /// Gets the hashed string.
  109. /// </summary>
  110. public string GetHashedString(User user, string str)
  111. {
  112. if (string.IsNullOrEmpty(user.Password))
  113. {
  114. return _cryptographyProvider.CreatePasswordHash(str).ToString();
  115. }
  116. // TODO: make use of iterations parameter?
  117. PasswordHash passwordHash = PasswordHash.Parse(user.Password);
  118. return new PasswordHash(
  119. passwordHash.Id,
  120. _cryptographyProvider.ComputeHash(
  121. passwordHash.Id,
  122. Encoding.UTF8.GetBytes(str),
  123. passwordHash.Salt),
  124. passwordHash.Salt,
  125. passwordHash.Parameters.ToDictionary(x => x.Key, y => y.Value)).ToString();
  126. }
  127. public byte[] GetHashed(User user, string str)
  128. {
  129. if (string.IsNullOrEmpty(user.Password))
  130. {
  131. return _cryptographyProvider.CreatePasswordHash(str).Hash;
  132. }
  133. // TODO: make use of iterations parameter?
  134. PasswordHash passwordHash = PasswordHash.Parse(user.Password);
  135. return _cryptographyProvider.ComputeHash(
  136. passwordHash.Id,
  137. Encoding.UTF8.GetBytes(str),
  138. passwordHash.Salt);
  139. }
  140. }
  141. }