< Summary - Jellyfin

Information
Class: Jellyfin.Server.Implementations.Users.UserManager
Assembly: Jellyfin.Server.Implementations
File(s): /srv/git/jellyfin/Jellyfin.Server.Implementations/Users/UserManager.cs
Line coverage
46%
Covered lines: 245
Uncovered lines: 284
Coverable lines: 529
Total lines: 993
Line coverage: 46.3%
Branch coverage
37%
Covered branches: 61
Total branches: 164
Branch coverage: 37.1%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 2/13/2026 - 12:11:21 AM Line coverage: 70.9% (117/165) Branch coverage: 42.5% (17/40) Total lines: 8944/19/2026 - 12:14:27 AM Line coverage: 44.4% (215/484) Branch coverage: 39% (57/146) Total lines: 8945/4/2026 - 12:15:16 AM Line coverage: 44.2% (215/486) Branch coverage: 39% (57/146) Total lines: 8965/7/2026 - 12:15:44 AM Line coverage: 46.3% (245/529) Branch coverage: 39.6% (65/164) Total lines: 9935/20/2026 - 12:15:44 AM Line coverage: 46.3% (245/529) Branch coverage: 37.1% (61/164) Total lines: 993 2/13/2026 - 12:11:21 AM Line coverage: 70.9% (117/165) Branch coverage: 42.5% (17/40) Total lines: 8944/19/2026 - 12:14:27 AM Line coverage: 44.4% (215/484) Branch coverage: 39% (57/146) Total lines: 8945/4/2026 - 12:15:16 AM Line coverage: 44.2% (215/486) Branch coverage: 39% (57/146) Total lines: 8965/7/2026 - 12:15:44 AM Line coverage: 46.3% (245/529) Branch coverage: 39.6% (65/164) Total lines: 9935/20/2026 - 12:15:44 AM Line coverage: 46.3% (245/529) Branch coverage: 37.1% (61/164) Total lines: 993

Coverage delta

Coverage delta 27 -27

Metrics

File(s)

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

#LineLine coverage
 1#pragma warning disable CA1307
 2#pragma warning disable RS0030 // Do not use banned APIs
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Globalization;
 7using System.Linq;
 8using System.Text.RegularExpressions;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using AsyncKeyedLock;
 12using Jellyfin.Data;
 13using Jellyfin.Data.Enums;
 14using Jellyfin.Data.Events;
 15using Jellyfin.Data.Events.Users;
 16using Jellyfin.Database.Implementations;
 17using Jellyfin.Database.Implementations.Entities;
 18using Jellyfin.Database.Implementations.Enums;
 19using Jellyfin.Extensions;
 20using MediaBrowser.Common;
 21using MediaBrowser.Common.Extensions;
 22using MediaBrowser.Common.Net;
 23using MediaBrowser.Controller.Authentication;
 24using MediaBrowser.Controller.Configuration;
 25using MediaBrowser.Controller.Drawing;
 26using MediaBrowser.Controller.Events;
 27using MediaBrowser.Controller.Library;
 28using MediaBrowser.Controller.Net;
 29using MediaBrowser.Model.Configuration;
 30using MediaBrowser.Model.Dto;
 31using MediaBrowser.Model.Users;
 32using Microsoft.EntityFrameworkCore;
 33using Microsoft.Extensions.Logging;
 34
 35namespace Jellyfin.Server.Implementations.Users
 36{
 37    /// <summary>
 38    /// Manages the creation and retrieval of <see cref="User"/> instances.
 39    /// </summary>
 40    public partial class UserManager : IUserManager, IDisposable
 41    {
 42        private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
 43        private readonly IEventManager _eventManager;
 44        private readonly INetworkManager _networkManager;
 45        private readonly IApplicationHost _appHost;
 46        private readonly IImageProcessor _imageProcessor;
 47        private readonly ILogger<UserManager> _logger;
 48        private readonly IReadOnlyCollection<IPasswordResetProvider> _passwordResetProviders;
 49        private readonly IReadOnlyCollection<IAuthenticationProvider> _authenticationProviders;
 50        private readonly InvalidAuthProvider _invalidAuthProvider;
 51        private readonly DefaultAuthenticationProvider _defaultAuthenticationProvider;
 52        private readonly DefaultPasswordResetProvider _defaultPasswordResetProvider;
 53        private readonly IServerConfigurationManager _serverConfigurationManager;
 54
 2155        private readonly AsyncKeyedLocker<Guid> _userLock = new();
 56
 57        /// <summary>
 58        /// Initializes a new instance of the <see cref="UserManager"/> class.
 59        /// </summary>
 60        /// <param name="dbProvider">The database provider.</param>
 61        /// <param name="eventManager">The event manager.</param>
 62        /// <param name="networkManager">The network manager.</param>
 63        /// <param name="appHost">The application host.</param>
 64        /// <param name="imageProcessor">The image processor.</param>
 65        /// <param name="logger">The logger.</param>
 66        /// <param name="serverConfigurationManager">The system config manager.</param>
 67        /// <param name="passwordResetProviders">The password reset providers.</param>
 68        /// <param name="authenticationProviders">The authentication providers.</param>
 69        public UserManager(
 70            IDbContextFactory<JellyfinDbContext> dbProvider,
 71            IEventManager eventManager,
 72            INetworkManager networkManager,
 73            IApplicationHost appHost,
 74            IImageProcessor imageProcessor,
 75            ILogger<UserManager> logger,
 76            IServerConfigurationManager serverConfigurationManager,
 77            IEnumerable<IPasswordResetProvider> passwordResetProviders,
 78            IEnumerable<IAuthenticationProvider> authenticationProviders)
 79        {
 2180            _dbProvider = dbProvider;
 2181            _eventManager = eventManager;
 2182            _networkManager = networkManager;
 2183            _appHost = appHost;
 2184            _imageProcessor = imageProcessor;
 2185            _logger = logger;
 2186            _serverConfigurationManager = serverConfigurationManager;
 87
 2188            _passwordResetProviders = passwordResetProviders.ToList();
 2189            _authenticationProviders = authenticationProviders.ToList();
 90
 2191            _invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
 2192            _defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
 2193            _defaultPasswordResetProvider = _passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
 2194        }
 95
 96        /// <inheritdoc/>
 97        public event EventHandler<GenericEventArgs<User>>? OnUserUpdated;
 98
 99        /// <inheritdoc/>
 100        public IEnumerable<User> GetUsers()
 101        {
 3102            using var dbContext = _dbProvider.CreateDbContext();
 3103            return UserQuery(dbContext)
 3104                .ToArray();
 3105        }
 106
 107        /// <inheritdoc/>
 108        public IEnumerable<Guid> GetUsersIds()
 109        {
 0110            using var dbContext = _dbProvider.CreateDbContext();
 0111            return dbContext.Users
 0112                .AsNoTracking()
 0113                .Select(user => user.Id)
 0114                .ToArray();
 0115        }
 116
 117        // This is some regex that matches only on unicode "word" characters, as well as -, _ and @
 118        // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
 119        // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes
 120        [GeneratedRegex(@"^(?!\s)[\w\ \-'._@+]+(?<!\s)$")]
 121        private static partial Regex ValidUsernameRegex();
 122
 123        /// <inheritdoc/>
 124        public User? GetUserById(Guid id)
 125        {
 252126            if (id.IsEmpty())
 127            {
 0128                throw new ArgumentException("Guid can't be empty", nameof(id));
 129            }
 130
 252131            using var dbContext = _dbProvider.CreateDbContext();
 252132            return UserQuery(dbContext)
 252133                .FirstOrDefault(user => user.Id == id);
 252134        }
 135
 136        private static IQueryable<User> UserQuery(JellyfinDbContext dbContext)
 137        {
 306138            return dbContext.Users
 306139                            .AsSingleQuery()
 306140                            .Include(user => user.Permissions)
 306141                            .Include(user => user.Preferences)
 306142                            .Include(user => user.AccessSchedules)
 306143                            .Include(user => user.ProfileImage)
 306144                            .AsNoTracking();
 145        }
 146
 147        /// <inheritdoc/>
 148        public User? GetFirstUser()
 149        {
 18150            using var dbContext = _dbProvider.CreateDbContext();
 18151            return UserQuery(dbContext).FirstOrDefault();
 18152        }
 153
 154        /// <inheritdoc/>
 155        public User? GetUserByName(string name)
 156        {
 30157            if (string.IsNullOrWhiteSpace(name))
 158            {
 0159                throw new ArgumentException("Invalid username", nameof(name));
 160            }
 161
 30162            using var dbContext = _dbProvider.CreateDbContext();
 163#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string compari
 164#pragma warning disable CA1311 // Specify a culture or use an invariant version to avoid implicit dependency on current 
 165#pragma warning disable CA1304 // The behavior of 'string.ToUpper()' could vary based on the current user's locale setti
 30166            return UserQuery(dbContext)
 30167                .FirstOrDefault(u => u.Username.ToUpper() == name.ToUpper());
 168#pragma warning restore CA1304 // The behavior of 'string.ToUpper()' could vary based on the current user's locale setti
 169#pragma warning restore CA1311 // Specify a culture or use an invariant version to avoid implicit dependency on current 
 170#pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string compari
 30171        }
 172
 173        /// <inheritdoc/>
 174        public async Task RenameUser(Guid userId, string oldName, string newName)
 175        {
 0176            ThrowIfInvalidUsername(newName);
 177
 0178            if (oldName.Equals(newName, StringComparison.OrdinalIgnoreCase))
 179            {
 0180                throw new ArgumentException("The new and old names must be different.");
 181            }
 182
 0183            User user = null!; // user is never actually null where its used afterwards so we can just ignore.
 0184            using (await _userLock.LockAsync(userId).ConfigureAwait(false))
 185            {
 0186                var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 0187                await using (dbContext.ConfigureAwait(false))
 188                {
 189#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string compari
 190#pragma warning disable CA1311 // Specify a culture or use an invariant version to avoid implicit dependency on current 
 191#pragma warning disable CA1304 // The behavior of 'string.ToUpper()' could vary based on the current user's locale setti
 0192                    if (await dbContext.Users
 0193                            .AnyAsync(u => u.Username.ToUpper() == newName.ToUpper() && u.Id != userId)
 0194                            .ConfigureAwait(false))
 195                    {
 0196                        throw new ArgumentException(string.Format(
 0197                            CultureInfo.InvariantCulture,
 0198                            "A user with the name '{0}' already exists.",
 0199                            newName));
 200                    }
 201#pragma warning restore CA1304 // The behavior of 'string.ToUpper()' could vary based on the current user's locale setti
 202#pragma warning restore CA1311 // Specify a culture or use an invariant version to avoid implicit dependency on current 
 203#pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string compari
 204
 0205                    user = await UserQuery(dbContext)
 0206                        .AsTracking()
 0207                        .FirstOrDefaultAsync(u => u.Id == userId)
 0208                        .ConfigureAwait(false)
 0209                        ?? throw new ResourceNotFoundException(nameof(userId));
 0210                    user.Username = newName;
 0211                    await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false);
 212                }
 0213            }
 214
 0215            var eventArgs = new UserUpdatedEventArgs(user);
 0216            await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
 0217            OnUserUpdated?.Invoke(this, eventArgs);
 0218        }
 219
 220        /// <inheritdoc/>
 221        public async Task UpdateUserAsync(User user)
 222        {
 1223            using (await _userLock.LockAsync(user.Id).ConfigureAwait(false))
 224            {
 1225                await UpdateUserInternalAsync(user).ConfigureAwait(false);
 1226            }
 1227        }
 228
 229        internal async Task<User> CreateUserInternalAsync(string name, JellyfinDbContext dbContext)
 230        {
 231            // TODO: Remove after user item data is migrated.
 17232            var max = await dbContext.Users.AsQueryable().AnyAsync().ConfigureAwait(false)
 17233                ? await dbContext.Users.AsQueryable().Select(u => u.InternalId).MaxAsync().ConfigureAwait(false)
 17234                : 0;
 235
 17236            var user = new User(
 17237                name,
 17238                _defaultAuthenticationProvider.GetType().FullName!,
 17239                _defaultPasswordResetProvider.GetType().FullName!)
 17240            {
 17241                InternalId = max + 1
 17242            };
 243
 17244            user.AddDefaultPermissions();
 17245            user.AddDefaultPreferences();
 246
 17247            return user;
 17248        }
 249
 250        /// <inheritdoc/>
 251        public async Task<User> CreateUserAsync(string name)
 252        {
 2253            ThrowIfInvalidUsername(name);
 254
 255            User newUser;
 1256            var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 1257            await using (dbContext.ConfigureAwait(false))
 258            {
 259#pragma warning disable CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string compari
 260#pragma warning disable CA1311 // Specify a culture or use an invariant version to avoid implicit dependency on current 
 261#pragma warning disable CA1304 // The behavior of 'string.ToUpper()' could vary based on the current user's locale setti
 1262                if (await dbContext.Users
 1263                        .AnyAsync(u => u.Username.ToUpper() == name.ToUpper())
 1264                        .ConfigureAwait(false))
 265                {
 0266                    throw new ArgumentException(string.Format(
 0267                        CultureInfo.InvariantCulture,
 0268                        "A user with the name '{0}' already exists.",
 0269                        name));
 270                }
 271#pragma warning restore CA1304 // The behavior of 'string.ToUpper()' could vary based on the current user's locale setti
 272#pragma warning restore CA1311 // Specify a culture or use an invariant version to avoid implicit dependency on current 
 273#pragma warning restore CA1862 // Use the 'StringComparison' method overloads to perform case-insensitive string compari
 274
 1275                newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false);
 276
 1277                dbContext.Users.Add(newUser);
 1278                await dbContext.SaveChangesAsync().ConfigureAwait(false);
 279            }
 280
 1281            await _eventManager.PublishAsync(new UserCreatedEventArgs(newUser)).ConfigureAwait(false);
 282
 1283            return newUser;
 1284        }
 285
 286        /// <inheritdoc/>
 287        public async Task DeleteUserAsync(Guid userId)
 288        {
 289            User? user;
 0290            using (await _userLock.LockAsync(userId).ConfigureAwait(false))
 291            {
 0292                var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 0293                await using (dbContext.ConfigureAwait(false))
 294                {
 0295                    user = await dbContext.Users
 0296                        .Include(u => u.Permissions)
 0297                        .FirstOrDefaultAsync(u => u.Id.Equals(userId))
 0298                        .ConfigureAwait(false);
 0299                    if (user is null)
 300                    {
 0301                        throw new ResourceNotFoundException(nameof(userId));
 302                    }
 303
 0304                    var userCount = await dbContext.Users.CountAsync().ConfigureAwait(false);
 0305                    if (userCount == 1)
 306                    {
 0307                        throw new InvalidOperationException(string.Format(
 0308                            CultureInfo.InvariantCulture,
 0309                            "The user '{0}' cannot be deleted because there must be at least one user in the system.",
 0310                            user.Username));
 311                    }
 312
 0313                    if (user.HasPermission(PermissionKind.IsAdministrator)
 0314                        && await dbContext.Users
 0315                            .CountAsync(i => i.Permissions.Any(p => p.Kind == PermissionKind.IsAdministrator && p.Value)
 0316                            .ConfigureAwait(false) == 1)
 317                    {
 0318                        throw new ArgumentException(
 0319                            string.Format(
 0320                                CultureInfo.InvariantCulture,
 0321                                "The user '{0}' cannot be deleted because there must be at least one admin user in the s
 0322                                user.Username),
 0323                            nameof(userId));
 324                    }
 325
 0326                    dbContext.Users.Remove(user);
 0327                    await dbContext.SaveChangesAsync().ConfigureAwait(false);
 328                }
 0329            }
 330
 0331            await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false);
 0332        }
 333
 334        /// <inheritdoc/>
 335        public Task ResetPassword(Guid userId)
 336        {
 0337            return ChangePassword(userId, string.Empty);
 338        }
 339
 340        /// <inheritdoc/>
 341        public async Task ChangePassword(Guid userId, string newPassword)
 342        {
 3343            User dbUser = null!;
 3344            using (await _userLock.LockAsync(userId).ConfigureAwait(false))
 345            {
 3346                var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 3347                await using (dbContext.ConfigureAwait(false))
 348                {
 3349                    dbUser = await UserQuery(dbContext)
 3350                        .AsTracking()
 3351                        .FirstOrDefaultAsync(u => u.Id == userId)
 3352                        .ConfigureAwait(false)
 3353                        ?? throw new ResourceNotFoundException(nameof(userId));
 3354                    if (dbUser.HasPermission(PermissionKind.IsAdministrator) && string.IsNullOrWhiteSpace(newPassword))
 355                    {
 0356                        throw new ArgumentException("Admin user passwords must not be empty", nameof(newPassword));
 357                    }
 358
 3359                    await GetAuthenticationProvider(dbUser).ChangePassword(dbUser, newPassword).ConfigureAwait(false);
 3360                    await dbContext.SaveChangesAsync().ConfigureAwait(false);
 361                }
 3362            }
 363
 3364            await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(dbUser)).ConfigureAwait(false);
 3365        }
 366
 367        /// <inheritdoc/>
 368        public UserDto GetUserDto(User user, string? remoteEndPoint = null)
 369        {
 34370            var castReceiverApplications = _serverConfigurationManager.Configuration.CastReceiverApplications;
 34371            return new UserDto
 34372            {
 34373                Name = user.Username,
 34374                Id = user.Id,
 34375                ServerId = _appHost.SystemId,
 34376                EnableAutoLogin = user.EnableAutoLogin,
 34377                LastLoginDate = user.LastLoginDate,
 34378                LastActivityDate = user.LastActivityDate,
 34379                PrimaryImageTag = user.ProfileImage is not null ? _imageProcessor.GetImageCacheTag(user) : null,
 34380                Configuration = new UserConfiguration
 34381                {
 34382                    SubtitleMode = user.SubtitleMode,
 34383                    HidePlayedInLatest = user.HidePlayedInLatest,
 34384                    EnableLocalPassword = user.EnableLocalPassword,
 34385                    PlayDefaultAudioTrack = user.PlayDefaultAudioTrack,
 34386                    DisplayCollectionsView = user.DisplayCollectionsView,
 34387                    DisplayMissingEpisodes = user.DisplayMissingEpisodes,
 34388                    AudioLanguagePreference = user.AudioLanguagePreference,
 34389                    RememberAudioSelections = user.RememberAudioSelections,
 34390                    EnableNextEpisodeAutoPlay = user.EnableNextEpisodeAutoPlay,
 34391                    RememberSubtitleSelections = user.RememberSubtitleSelections,
 34392                    SubtitleLanguagePreference = user.SubtitleLanguagePreference ?? string.Empty,
 34393                    OrderedViews = user.GetPreferenceValues<Guid>(PreferenceKind.OrderedViews),
 34394                    GroupedFolders = user.GetPreferenceValues<Guid>(PreferenceKind.GroupedFolders),
 34395                    MyMediaExcludes = user.GetPreferenceValues<Guid>(PreferenceKind.MyMediaExcludes),
 34396                    LatestItemsExcludes = user.GetPreferenceValues<Guid>(PreferenceKind.LatestItemExcludes),
 34397                    CastReceiverId = string.IsNullOrEmpty(user.CastReceiverId)
 34398                        ? castReceiverApplications.FirstOrDefault()?.Id
 34399                        : castReceiverApplications.FirstOrDefault(c => string.Equals(c.Id, user.CastReceiverId, StringCo
 34400                          ?? castReceiverApplications.FirstOrDefault()?.Id
 34401                },
 34402                Policy = new UserPolicy
 34403                {
 34404                    MaxParentalRating = user.MaxParentalRatingScore,
 34405                    MaxParentalSubRating = user.MaxParentalRatingSubScore,
 34406                    EnableUserPreferenceAccess = user.EnableUserPreferenceAccess,
 34407                    RemoteClientBitrateLimit = user.RemoteClientBitrateLimit ?? 0,
 34408                    AuthenticationProviderId = user.AuthenticationProviderId,
 34409                    PasswordResetProviderId = user.PasswordResetProviderId,
 34410                    InvalidLoginAttemptCount = user.InvalidLoginAttemptCount,
 34411                    LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout ?? -1,
 34412                    MaxActiveSessions = user.MaxActiveSessions,
 34413                    IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator),
 34414                    IsHidden = user.HasPermission(PermissionKind.IsHidden),
 34415                    IsDisabled = user.HasPermission(PermissionKind.IsDisabled),
 34416                    EnableSharedDeviceControl = user.HasPermission(PermissionKind.EnableSharedDeviceControl),
 34417                    EnableRemoteAccess = user.HasPermission(PermissionKind.EnableRemoteAccess),
 34418                    EnableLiveTvManagement = user.HasPermission(PermissionKind.EnableLiveTvManagement),
 34419                    EnableLiveTvAccess = user.HasPermission(PermissionKind.EnableLiveTvAccess),
 34420                    EnableMediaPlayback = user.HasPermission(PermissionKind.EnableMediaPlayback),
 34421                    EnableAudioPlaybackTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding),
 34422                    EnableVideoPlaybackTranscoding = user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding),
 34423                    EnableContentDeletion = user.HasPermission(PermissionKind.EnableContentDeletion),
 34424                    EnableContentDownloading = user.HasPermission(PermissionKind.EnableContentDownloading),
 34425                    EnableSyncTranscoding = user.HasPermission(PermissionKind.EnableSyncTranscoding),
 34426                    EnableMediaConversion = user.HasPermission(PermissionKind.EnableMediaConversion),
 34427                    EnableAllChannels = user.HasPermission(PermissionKind.EnableAllChannels),
 34428                    EnableAllDevices = user.HasPermission(PermissionKind.EnableAllDevices),
 34429                    EnableAllFolders = user.HasPermission(PermissionKind.EnableAllFolders),
 34430                    EnableRemoteControlOfOtherUsers = user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers)
 34431                    EnablePlaybackRemuxing = user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
 34432                    ForceRemoteSourceTranscoding = user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding),
 34433                    EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing),
 34434                    EnableCollectionManagement = user.HasPermission(PermissionKind.EnableCollectionManagement),
 34435                    EnableSubtitleManagement = user.HasPermission(PermissionKind.EnableSubtitleManagement),
 34436                    AccessSchedules = user.AccessSchedules.ToArray(),
 34437                    BlockedTags = user.GetPreference(PreferenceKind.BlockedTags),
 34438                    AllowedTags = user.GetPreference(PreferenceKind.AllowedTags),
 34439                    EnabledChannels = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels),
 34440                    EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices),
 34441                    EnabledFolders = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders),
 34442                    EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolder
 34443                    SyncPlayAccess = user.SyncPlayAccess,
 34444                    BlockedChannels = user.GetPreferenceValues<Guid>(PreferenceKind.BlockedChannels),
 34445                    BlockedMediaFolders = user.GetPreferenceValues<Guid>(PreferenceKind.BlockedMediaFolders),
 34446                    BlockUnratedItems = user.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems)
 34447                }
 34448            };
 449        }
 450
 451        /// <inheritdoc/>
 452        public async Task<User?> AuthenticateUser(
 453            string username,
 454            string password,
 455            string remoteEndPoint,
 456            bool isUserSession)
 457        {
 15458            if (string.IsNullOrWhiteSpace(username))
 459            {
 0460                _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndP
 0461                throw new ArgumentNullException(nameof(username));
 462            }
 463
 464            bool success;
 15465            var user = GetUserByName(username);
 15466            using (await _userLock.LockAsync(user?.Id ?? Guid.Empty).ConfigureAwait(false))
 467            {
 468                // Reload the user now that we hold the lock so the RowVersion is current.
 469                // GetUserByName uses AsNoTracking and the snapshot may be stale if another
 470                // write (e.g. a concurrent login) incremented RowVersion after our initial load.
 15471                if (user is not null)
 472                {
 15473                    user = GetUserById(user.Id) ?? user;
 474                }
 475
 15476                var authResult = await AuthenticateLocalUser(username, password, user)
 15477                    .ConfigureAwait(false);
 15478                var authenticationProvider = authResult.AuthenticationProvider;
 15479                success = authResult.Success;
 480
 15481                if (user is null)
 482                {
 0483                    string updatedUsername = authResult.Username;
 484
 0485                    if (success
 0486                        && authenticationProvider is not null
 0487                        && authenticationProvider is not DefaultAuthenticationProvider)
 488                    {
 489                        // Trust the username returned by the authentication provider
 0490                        username = updatedUsername;
 491
 492                        // Search the database for the user again
 493                        // the authentication provider might have created it
 0494                        user = GetUserByName(username);
 495
 0496                        if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy && user is not null)
 497                        {
 0498                            await UpdatePolicyAsync(user.Id, hasNewUserPolicy.GetNewUserPolicy()).ConfigureAwait(false);
 499                        }
 500                    }
 501                }
 502
 15503                if (success && user is not null && authenticationProvider is not null)
 504                {
 15505                    var providerId = authenticationProvider.GetType().FullName;
 506
 15507                    if (providerId is not null && !string.Equals(providerId, user.AuthenticationProviderId, StringCompar
 508                    {
 0509                        user.AuthenticationProviderId = providerId;
 0510                        await UpdateUserInternalAsync(user).ConfigureAwait(false);
 511                    }
 512                }
 513
 15514                if (user is null)
 515                {
 0516                    _logger.LogInformation(
 0517                        "Authentication request for {UserName} has been denied (IP: {IP}).",
 0518                        username,
 0519                        remoteEndPoint);
 0520                    throw new AuthenticationException("Invalid username or password entered.");
 521                }
 522
 15523                if (user.HasPermission(PermissionKind.IsDisabled))
 524                {
 0525                    _logger.LogInformation(
 0526                        "Authentication request for {UserName} has been denied because this account is currently disable
 0527                        username,
 0528                        remoteEndPoint);
 0529                    throw new SecurityException(
 0530                        $"The {user.Username} account is currently disabled. Please consult with your administrator.");
 531                }
 532
 15533                if (!user.HasPermission(PermissionKind.EnableRemoteAccess) &&
 15534                    !_networkManager.IsInLocalNetwork(remoteEndPoint))
 535                {
 0536                    _logger.LogInformation(
 0537                        "Authentication request for {UserName} forbidden: remote access disabled and user not in local n
 0538                        username,
 0539                        remoteEndPoint);
 0540                    throw new SecurityException("Forbidden.");
 541                }
 542
 15543                if (!user.IsParentalScheduleAllowed())
 544                {
 0545                    _logger.LogInformation(
 0546                        "Authentication request for {UserName} is not allowed at this time due parental restrictions (IP
 0547                        username,
 0548                        remoteEndPoint);
 0549                    throw new SecurityException("User is not allowed access at this time.");
 550                }
 551
 552                // Update LastActivityDate and LastLoginDate, then save
 15553                if (success)
 554                {
 15555                    if (isUserSession)
 556                    {
 15557                        user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
 558                    }
 559
 15560                    user.InvalidLoginAttemptCount = 0;
 15561                    await UpdateUserInternalAsync(user).ConfigureAwait(false);
 15562                    _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Username);
 563                }
 564                else
 565                {
 0566                    await IncrementInvalidLoginAttemptCount(user).ConfigureAwait(false);
 0567                    _logger.LogInformation(
 0568                        "Authentication request for {UserName} has been denied (IP: {IP}).",
 0569                        user.Username,
 0570                        remoteEndPoint);
 571                }
 15572            }
 573
 15574            return success ? user : null;
 15575        }
 576
 577        /// <inheritdoc/>
 578        public async Task<ForgotPasswordResult> StartForgotPasswordProcess(string enteredUsername, bool isInNetwork)
 579        {
 0580            var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername);
 0581            var passwordResetProvider = GetPasswordResetProvider(user);
 582
 0583            var result = await passwordResetProvider
 0584                .StartForgotPasswordProcess(user, enteredUsername, isInNetwork)
 0585                .ConfigureAwait(false);
 586
 0587            if (user is not null && isInNetwork)
 588            {
 0589                await UpdateUserAsync(user).ConfigureAwait(false);
 590            }
 591
 0592            return result;
 0593        }
 594
 595        /// <inheritdoc/>
 596        public async Task<PinRedeemResult> RedeemPasswordResetPin(string pin)
 597        {
 0598            foreach (var provider in _passwordResetProviders)
 599            {
 0600                var result = await provider.RedeemPasswordResetPin(pin).ConfigureAwait(false);
 601
 0602                if (result.Success)
 603                {
 0604                    return result;
 605                }
 606            }
 607
 0608            return new PinRedeemResult();
 0609        }
 610
 611        /// <inheritdoc />
 612        public async Task InitializeAsync()
 613        {
 614            // TODO: Refactor the startup wizard so that it doesn't require a user to already exist.
 17615            var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 17616            await using (dbContext.ConfigureAwait(false))
 617            {
 17618                if (await dbContext.Users.AnyAsync().ConfigureAwait(false))
 619                {
 620                    return;
 621                }
 622
 16623                var defaultName = Environment.UserName;
 16624                if (string.IsNullOrWhiteSpace(defaultName) || !ValidUsernameRegex().IsMatch(defaultName))
 625                {
 0626                    defaultName = "MyJellyfinUser";
 627                }
 628
 16629                _logger.LogWarning("No users, creating one with username {UserName}", defaultName);
 630
 16631                var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false);
 16632                newUser.SetPermission(PermissionKind.IsAdministrator, true);
 16633                newUser.SetPermission(PermissionKind.EnableContentDeletion, true);
 16634                newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true);
 635
 16636                dbContext.Users.Add(newUser);
 16637                await dbContext.SaveChangesAsync().ConfigureAwait(false);
 638            }
 17639        }
 640
 641        /// <inheritdoc/>
 642        public NameIdPair[] GetAuthenticationProviders()
 643        {
 0644            return _authenticationProviders
 0645                .Where(provider => provider.IsEnabled)
 0646                .OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1)
 0647                .ThenBy(i => i.Name)
 0648                .Select(i => new NameIdPair
 0649                {
 0650                    Name = i.Name,
 0651                    Id = i.GetType().FullName
 0652                })
 0653                .ToArray();
 654        }
 655
 656        /// <inheritdoc/>
 657        public NameIdPair[] GetPasswordResetProviders()
 658        {
 0659            return _passwordResetProviders
 0660                .Where(provider => provider.IsEnabled)
 0661                .OrderBy(i => i is DefaultPasswordResetProvider ? 0 : 1)
 0662                .ThenBy(i => i.Name)
 0663                .Select(i => new NameIdPair
 0664                {
 0665                    Name = i.Name,
 0666                    Id = i.GetType().FullName
 0667                })
 0668                .ToArray();
 669        }
 670
 671        /// <inheritdoc/>
 672        public async Task UpdateConfigurationAsync(Guid userId, UserConfiguration config)
 673        {
 0674            using (await _userLock.LockAsync(userId).ConfigureAwait(false))
 675            {
 0676                var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 0677                await using (dbContext.ConfigureAwait(false))
 678                {
 0679                    var user = UserQuery(dbContext)
 0680                                   .AsTracking()
 0681                                   .FirstOrDefault(u => u.Id.Equals(userId))
 0682                               ?? throw new ArgumentException("No user exists with given Id!");
 683
 0684                    user.SubtitleMode = config.SubtitleMode;
 0685                    user.HidePlayedInLatest = config.HidePlayedInLatest;
 0686                    user.EnableLocalPassword = config.EnableLocalPassword;
 0687                    user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack;
 0688                    user.DisplayCollectionsView = config.DisplayCollectionsView;
 0689                    user.DisplayMissingEpisodes = config.DisplayMissingEpisodes;
 0690                    user.AudioLanguagePreference = config.AudioLanguagePreference;
 0691                    user.RememberAudioSelections = config.RememberAudioSelections;
 0692                    user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay;
 0693                    user.RememberSubtitleSelections = config.RememberSubtitleSelections;
 0694                    user.SubtitleLanguagePreference = config.SubtitleLanguagePreference;
 695
 696                    // Only set cast receiver id if it is passed in and it exists in the server config.
 0697                    if (!string.IsNullOrEmpty(config.CastReceiverId)
 0698                        && _serverConfigurationManager.Configuration.CastReceiverApplications.Any(c => string.Equals(c.I
 699                    {
 0700                        user.CastReceiverId = config.CastReceiverId;
 701                    }
 702
 0703                    user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
 0704                    user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
 0705                    user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
 0706                    user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
 707
 0708                    dbContext.Update(user);
 0709                    await dbContext.SaveChangesAsync().ConfigureAwait(false);
 710                }
 0711            }
 0712        }
 713
 714        /// <inheritdoc/>
 715        public async Task UpdatePolicyAsync(Guid userId, UserPolicy policy)
 716        {
 0717            using (await _userLock.LockAsync(userId).ConfigureAwait(false))
 718            {
 0719                var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 0720                await using (dbContext.ConfigureAwait(false))
 721                {
 0722                    var user = UserQuery(dbContext)
 0723                        .AsTracking()
 0724                        .FirstOrDefault(u => u.Id.Equals(userId))
 0725                        ?? throw new ArgumentException("No user exists with given Id!");
 726
 727                    // The default number of login attempts is 3, but for some god forsaken reason it's sent to the serv
 0728                    int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch
 0729                    {
 0730                        -1 => null,
 0731                        0 => 3,
 0732                        _ => policy.LoginAttemptsBeforeLockout
 0733                    };
 734
 0735                    user.MaxParentalRatingScore = policy.MaxParentalRating;
 0736                    user.MaxParentalRatingSubScore = policy.MaxParentalSubRating;
 0737                    user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
 0738                    user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
 0739                    user.AuthenticationProviderId = policy.AuthenticationProviderId;
 0740                    user.PasswordResetProviderId = policy.PasswordResetProviderId;
 0741                    user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount;
 0742                    user.LoginAttemptsBeforeLockout = maxLoginAttempts;
 0743                    user.MaxActiveSessions = policy.MaxActiveSessions;
 0744                    user.SyncPlayAccess = policy.SyncPlayAccess;
 0745                    user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
 0746                    user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
 0747                    user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
 0748                    user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
 0749                    user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
 0750                    user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
 0751                    user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
 0752                    user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
 0753                    user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscod
 0754                    user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscod
 0755                    user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
 0756                    user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
 0757                    user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
 0758                    user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
 0759                    user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
 0760                    user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
 0761                    user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
 0762                    user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOther
 0763                    user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
 0764                    user.SetPermission(PermissionKind.EnableCollectionManagement, policy.EnableCollectionManagement);
 0765                    user.SetPermission(PermissionKind.EnableSubtitleManagement, policy.EnableSubtitleManagement);
 0766                    user.SetPermission(PermissionKind.EnableLyricManagement, policy.EnableLyricManagement);
 0767                    user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding)
 0768                    user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
 769
 0770                    user.AccessSchedules.Clear();
 0771                    foreach (var policyAccessSchedule in policy.AccessSchedules)
 772                    {
 0773                        user.AccessSchedules.Add(policyAccessSchedule);
 774                    }
 775
 776                    // TODO: fix this at some point
 0777                    user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty<Unrated
 0778                    user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
 0779                    user.SetPreference(PreferenceKind.AllowedTags, policy.AllowedTags);
 0780                    user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
 0781                    user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
 0782                    user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
 0783                    user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFrom
 784
 0785                    dbContext.Update(user);
 0786                    await dbContext.SaveChangesAsync().ConfigureAwait(false);
 787                }
 0788            }
 0789        }
 790
 791        /// <inheritdoc/>
 792        public async Task ClearProfileImageAsync(User user)
 793        {
 0794            if (user.ProfileImage is null)
 795            {
 0796                return;
 797            }
 798
 0799            using (await _userLock.LockAsync(user.Id).ConfigureAwait(false))
 800            {
 0801                var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 0802                await using (dbContext.ConfigureAwait(false))
 803                {
 0804                    dbContext.Remove(user.ProfileImage);
 0805                    await dbContext.SaveChangesAsync().ConfigureAwait(false);
 806                }
 807
 0808                user.ProfileImage = null;
 0809            }
 0810        }
 811
 812        internal static void ThrowIfInvalidUsername(string name)
 813        {
 15814            if (!string.IsNullOrWhiteSpace(name) && ValidUsernameRegex().IsMatch(name))
 815            {
 7816                return;
 817            }
 818
 8819            throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (
 820        }
 821
 822        private IAuthenticationProvider GetAuthenticationProvider(User user)
 823        {
 3824            return GetAuthenticationProviders(user)[0];
 825        }
 826
 827        private IPasswordResetProvider GetPasswordResetProvider(User? user)
 828        {
 0829            if (user is null)
 830            {
 0831                return _defaultPasswordResetProvider;
 832            }
 833
 0834            return GetPasswordResetProviders(user)[0];
 835        }
 836
 837        private List<IAuthenticationProvider> GetAuthenticationProviders(User? user)
 838        {
 18839            var authenticationProviderId = user?.AuthenticationProviderId;
 840
 18841            var providers = _authenticationProviders.Where(i => i.IsEnabled).ToList();
 842
 18843            if (!string.IsNullOrEmpty(authenticationProviderId))
 844            {
 18845                providers = providers.Where(i => string.Equals(authenticationProviderId, i.GetType().FullName, StringCom
 846            }
 847
 18848            if (providers.Count == 0)
 849            {
 850                // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
 0851                _logger.LogWarning(
 0852                    "User {Username} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. 
 0853                    user?.Username,
 0854                    user?.AuthenticationProviderId);
 0855                providers = new List<IAuthenticationProvider>
 0856                {
 0857                    _invalidAuthProvider
 0858                };
 859            }
 860
 18861            return providers;
 862        }
 863
 864        private IPasswordResetProvider[] GetPasswordResetProviders(User user)
 865        {
 0866            var passwordResetProviderId = user.PasswordResetProviderId;
 0867            var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
 868
 0869            if (!string.IsNullOrEmpty(passwordResetProviderId))
 870            {
 0871                providers = providers.Where(i =>
 0872                        string.Equals(passwordResetProviderId, i.GetType().FullName, StringComparison.OrdinalIgnoreCase)
 0873                    .ToArray();
 874            }
 875
 0876            if (providers.Length == 0)
 877            {
 0878                providers = new IPasswordResetProvider[]
 0879                {
 0880                    _defaultPasswordResetProvider
 0881                };
 882            }
 883
 0884            return providers;
 885        }
 886
 887        private async Task<(IAuthenticationProvider? AuthenticationProvider, string Username, bool Success)> Authenticat
 888                string username,
 889                string password,
 890                User? user)
 891        {
 15892            bool success = false;
 15893            IAuthenticationProvider? authenticationProvider = null;
 894
 45895            foreach (var provider in GetAuthenticationProviders(user))
 896            {
 15897                var providerAuthResult =
 15898                    await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false);
 15899                var updatedUsername = providerAuthResult.Username;
 15900                success = providerAuthResult.Success;
 901
 15902                if (success)
 903                {
 15904                    authenticationProvider = provider;
 15905                    username = updatedUsername;
 15906                    break;
 907                }
 0908            }
 909
 15910            return (authenticationProvider, username, success);
 15911        }
 912
 913        private async Task<(string Username, bool Success)> AuthenticateWithProvider(
 914            IAuthenticationProvider provider,
 915            string username,
 916            string password,
 917            User? resolvedUser)
 918        {
 919            try
 920            {
 15921                var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser
 15922                    ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false)
 15923                    : await provider.Authenticate(username, password).ConfigureAwait(false);
 924
 15925                if (authenticationResult.Username != username)
 926                {
 0927                    _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Usern
 0928                    username = authenticationResult.Username;
 929                }
 930
 15931                return (username, true);
 932            }
 0933            catch (AuthenticationException ex)
 934            {
 0935                _logger.LogDebug(ex, "Error authenticating with provider {Provider}", provider.Name);
 936
 0937                return (username, false);
 938            }
 15939        }
 940
 941        private async Task IncrementInvalidLoginAttemptCount(User user)
 942        {
 0943            user.InvalidLoginAttemptCount++;
 0944            int? maxInvalidLogins = user.LoginAttemptsBeforeLockout;
 0945            if (maxInvalidLogins.HasValue && user.InvalidLoginAttemptCount >= maxInvalidLogins)
 946            {
 0947                user.SetPermission(PermissionKind.IsDisabled, true);
 0948                await _eventManager.PublishAsync(new UserLockedOutEventArgs(user)).ConfigureAwait(false);
 0949                _logger.LogWarning(
 0950                    "Disabling user {Username} due to {Attempts} unsuccessful login attempts.",
 0951                    user.Username,
 0952                    user.InvalidLoginAttemptCount);
 953            }
 954
 0955            await UpdateUserInternalAsync(user).ConfigureAwait(false);
 0956        }
 957
 958        private async Task UpdateUserInternalAsync(User user)
 959        {
 16960            var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
 16961            await using (dbContext.ConfigureAwait(false))
 962            {
 16963                await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false);
 964            }
 16965        }
 966
 967        private async Task UpdateUserInternalAsync(JellyfinDbContext dbContext, User user)
 968        {
 16969            dbContext.Users.Attach(user);
 16970            dbContext.Entry(user).State = EntityState.Modified;
 16971            await dbContext.SaveChangesAsync().ConfigureAwait(false);
 16972        }
 973
 974        /// <inheritdoc/>
 975        public void Dispose()
 976        {
 21977            Dispose(true);
 21978            GC.SuppressFinalize(this);
 21979        }
 980
 981        /// <summary>
 982        /// Disposes all members of this class.
 983        /// </summary>
 984        /// <param name="disposing">Defines if the class has been cleaned up by a dispose or finalizer.</param>
 985        protected virtual void Dispose(bool disposing)
 986        {
 21987            if (disposing)
 988            {
 21989                _userLock.Dispose();
 990            }
 21991        }
 992    }
 993}

Methods/Properties

.ctor(Microsoft.EntityFrameworkCore.IDbContextFactory`1<Jellyfin.Database.Implementations.JellyfinDbContext>,MediaBrowser.Controller.Events.IEventManager,MediaBrowser.Common.Net.INetworkManager,MediaBrowser.Common.IApplicationHost,MediaBrowser.Controller.Drawing.IImageProcessor,Microsoft.Extensions.Logging.ILogger`1<Jellyfin.Server.Implementations.Users.UserManager>,MediaBrowser.Controller.Configuration.IServerConfigurationManager,System.Collections.Generic.IEnumerable`1<MediaBrowser.Controller.Authentication.IPasswordResetProvider>,System.Collections.Generic.IEnumerable`1<MediaBrowser.Controller.Authentication.IAuthenticationProvider>)
GetUsers()
GetUsersIds()
GetUserById(System.Guid)
UserQuery(Jellyfin.Database.Implementations.JellyfinDbContext)
GetFirstUser()
GetUserByName(System.String)
RenameUser()
UpdateUserAsync()
CreateUserInternalAsync()
CreateUserAsync()
DeleteUserAsync()
ResetPassword(System.Guid)
ChangePassword()
GetUserDto(Jellyfin.Database.Implementations.Entities.User,System.String)
AuthenticateUser()
StartForgotPasswordProcess()
RedeemPasswordResetPin()
InitializeAsync()
GetAuthenticationProviders()
GetPasswordResetProviders()
UpdateConfigurationAsync()
UpdatePolicyAsync()
ClearProfileImageAsync()
ThrowIfInvalidUsername(System.String)
GetAuthenticationProvider(Jellyfin.Database.Implementations.Entities.User)
GetPasswordResetProvider(Jellyfin.Database.Implementations.Entities.User)
GetAuthenticationProviders(Jellyfin.Database.Implementations.Entities.User)
GetPasswordResetProviders(Jellyfin.Database.Implementations.Entities.User)
AuthenticateLocalUser()
AuthenticateWithProvider()
IncrementInvalidLoginAttemptCount()
UpdateUserInternalAsync()
UpdateUserInternalAsync()
Dispose()
Dispose(System.Boolean)