diff options
| author | crobibero <cody@robibe.ro> | 2020-06-24 10:52:39 -0600 |
|---|---|---|
| committer | crobibero <cody@robibe.ro> | 2020-06-24 10:52:39 -0600 |
| commit | cbcf3bfaffadf9d467b2bd0bb7f3e834d13d56d3 (patch) | |
| tree | 60c33f03ed003794eb61058ff870dc4f2dbee64d /Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs | |
| parent | 8f1505cdf5bc964c294cf0575807233f0e7af7d7 (diff) | |
| parent | 6ad50f2e023cdf8fedce8c828c507576d590858e (diff) | |
Merge remote-tracking branch 'upstream/api-migration' into api-channel
Diffstat (limited to 'Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs')
| -rw-r--r-- | Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs new file mode 100644 index 000000000..162dc6f5e --- /dev/null +++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs @@ -0,0 +1,141 @@ +#nullable enable + +using System; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; +using MediaBrowser.Common; +using MediaBrowser.Common.Cryptography; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Model.Cryptography; + +namespace Jellyfin.Server.Implementations.Users +{ + /// <summary> + /// The default authentication provider. + /// </summary> + public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser + { + private readonly ICryptoProvider _cryptographyProvider; + + /// <summary> + /// Initializes a new instance of the <see cref="DefaultAuthenticationProvider"/> class. + /// </summary> + /// <param name="cryptographyProvider">The cryptography provider.</param> + public DefaultAuthenticationProvider(ICryptoProvider cryptographyProvider) + { + _cryptographyProvider = cryptographyProvider; + } + + /// <inheritdoc /> + public string Name => "Default"; + + /// <inheritdoc /> + public bool IsEnabled => true; + + /// <inheritdoc /> + // This is dumb and an artifact of the backwards way auth providers were designed. + // This version of authenticate was never meant to be called, but needs to be here for interface compat + // Only the providers that don't provide local user support use this + public Task<ProviderAuthenticationResult> Authenticate(string username, string password) + { + throw new NotImplementedException(); + } + + /// <inheritdoc /> + // This is the version that we need to use for local users. Because reasons. + public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser) + { + if (resolvedUser == null) + { + throw new AuthenticationException("Specified user does not exist."); + } + + bool success = false; + + // As long as jellyfin supports passwordless users, we need this little block here to accommodate + if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) + { + return Task.FromResult(new ProviderAuthenticationResult + { + Username = username + }); + } + + byte[] passwordBytes = Encoding.UTF8.GetBytes(password); + + PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password); + if (_cryptographyProvider.GetSupportedHashMethods().Contains(readyHash.Id) + || _cryptographyProvider.DefaultHashMethod == readyHash.Id) + { + byte[] calculatedHash = _cryptographyProvider.ComputeHash( + readyHash.Id, + passwordBytes, + readyHash.Salt.ToArray()); + + if (readyHash.Hash.SequenceEqual(calculatedHash)) + { + success = true; + } + } + else + { + throw new AuthenticationException($"Requested crypto method not available in provider: {readyHash.Id}"); + } + + if (!success) + { + throw new AuthenticationException("Invalid username or password"); + } + + return Task.FromResult(new ProviderAuthenticationResult + { + Username = username + }); + } + + /// <inheritdoc /> + public bool HasPassword(User user) + => !string.IsNullOrEmpty(user?.Password); + + /// <inheritdoc /> + public Task ChangePassword(User user, string newPassword) + { + if (string.IsNullOrEmpty(newPassword)) + { + user.Password = null; + return Task.CompletedTask; + } + + PasswordHash newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword); + user.Password = newPasswordHash.ToString(); + + return Task.CompletedTask; + } + + /// <inheritdoc /> + public void ChangeEasyPassword(User user, string newPassword, string newPasswordHash) + { + if (newPassword != null) + { + newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword).ToString(); + } + + if (string.IsNullOrWhiteSpace(newPasswordHash)) + { + throw new ArgumentNullException(nameof(newPasswordHash)); + } + + user.EasyPassword = newPasswordHash; + } + + /// <inheritdoc /> + public string? GetEasyPasswordHash(User user) + { + return string.IsNullOrEmpty(user.EasyPassword) + ? null + : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash); + } + } +} |
