< Summary - Jellyfin

Information
Class: Jellyfin.Server.Implementations.Users.DefaultAuthenticationProvider
Assembly: Jellyfin.Server.Implementations
File(s): /srv/git/jellyfin/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
Line coverage
51%
Covered lines: 17
Uncovered lines: 16
Coverable lines: 33
Total lines: 115
Line coverage: 51.5%
Branch coverage
33%
Covered branches: 6
Total branches: 18
Branch coverage: 33.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Name()100%210%
get_IsEnabled()100%11100%
Authenticate(...)100%210%
Authenticate(...)21.42%81.231430%
HasPassword(...)50%22100%
ChangePassword(...)100%22100%

File(s)

/srv/git/jellyfin/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs

#LineLine coverage
 1using System;
 2using System.Diagnostics.CodeAnalysis;
 3using System.Globalization;
 4using System.Threading.Tasks;
 5using Jellyfin.Data.Entities;
 6using MediaBrowser.Controller.Authentication;
 7using MediaBrowser.Model.Cryptography;
 8using Microsoft.Extensions.Logging;
 9
 10namespace Jellyfin.Server.Implementations.Users
 11{
 12    /// <summary>
 13    /// The default authentication provider.
 14    /// </summary>
 15    public class DefaultAuthenticationProvider : IAuthenticationProvider, IRequiresResolvedUser
 16    {
 17        private readonly ILogger<DefaultAuthenticationProvider> _logger;
 18        private readonly ICryptoProvider _cryptographyProvider;
 19
 20        /// <summary>
 21        /// Initializes a new instance of the <see cref="DefaultAuthenticationProvider"/> class.
 22        /// </summary>
 23        /// <param name="logger">The logger.</param>
 24        /// <param name="cryptographyProvider">The cryptography provider.</param>
 25        public DefaultAuthenticationProvider(ILogger<DefaultAuthenticationProvider> logger, ICryptoProvider cryptography
 26        {
 2227            _logger = logger;
 2228            _cryptographyProvider = cryptographyProvider;
 2229        }
 30
 31        /// <inheritdoc />
 032        public string Name => "Default";
 33
 34        /// <inheritdoc />
 6235        public bool IsEnabled => true;
 36
 37        /// <inheritdoc />
 38        // This is dumb and an artifact of the backwards way auth providers were designed.
 39        // This version of authenticate was never meant to be called, but needs to be here for interface compat
 40        // Only the providers that don't provide local user support use this
 41        public Task<ProviderAuthenticationResult> Authenticate(string username, string password)
 42        {
 043            throw new NotImplementedException();
 44        }
 45
 46        /// <inheritdoc />
 47        // This is the version that we need to use for local users. Because reasons.
 48        public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User? resolvedUser)
 49        {
 50            [DoesNotReturn]
 51            static void ThrowAuthenticationException()
 52            {
 53                throw new AuthenticationException("Invalid username or password");
 54            }
 55
 1656            if (resolvedUser is null)
 57            {
 058                ThrowAuthenticationException();
 59            }
 60
 61            // As long as jellyfin supports password-less users, we need this little block here to accommodate
 1662            if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password))
 63            {
 1664                return Task.FromResult(new ProviderAuthenticationResult
 1665                {
 1666                    Username = username
 1667                });
 68            }
 69
 70            // Handle the case when the stored password is null, but the user tried to login with a password
 071            if (resolvedUser.Password is null)
 72            {
 073                ThrowAuthenticationException();
 74            }
 75
 076            PasswordHash readyHash = PasswordHash.Parse(resolvedUser.Password);
 077            if (!_cryptographyProvider.Verify(readyHash, password))
 78            {
 079                ThrowAuthenticationException();
 80            }
 81
 82            // Migrate old hashes to the new default
 083            if (!string.Equals(readyHash.Id, _cryptographyProvider.DefaultHashMethod, StringComparison.Ordinal)
 084                || int.Parse(readyHash.Parameters["iterations"], CultureInfo.InvariantCulture) != Constants.DefaultItera
 85            {
 086                _logger.LogInformation("Migrating password hash of {User} to the latest default", username);
 087                ChangePassword(resolvedUser, password);
 88            }
 89
 090            return Task.FromResult(new ProviderAuthenticationResult
 091            {
 092                Username = username
 093            });
 94        }
 95
 96        /// <inheritdoc />
 97        public bool HasPassword(User user)
 5998            => !string.IsNullOrEmpty(user?.Password);
 99
 100        /// <inheritdoc />
 101        public Task ChangePassword(User user, string newPassword)
 102        {
 3103            if (string.IsNullOrEmpty(newPassword))
 104            {
 1105                user.Password = null;
 1106                return Task.CompletedTask;
 107            }
 108
 2109            PasswordHash newPasswordHash = _cryptographyProvider.CreatePasswordHash(newPassword);
 2110            user.Password = newPasswordHash.ToString();
 111
 2112            return Task.CompletedTask;
 113        }
 114    }
 115}