From 3eeb6576d8425c8d2917f861b466dfa36e3994df Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 15 May 2020 17:24:01 -0400 Subject: Migrate User DB to EF Core --- .../Users/UserManager.cs | 763 +++++++++++++++++++++ 1 file changed, 763 insertions(+) create mode 100644 Jellyfin.Server.Implementations/Users/UserManager.cs (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs new file mode 100644 index 000000000..ddc05055b --- /dev/null +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -0,0 +1,763 @@ +#pragma warning disable CA1307 +#pragma warning disable CS1591 + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; +using MediaBrowser.Common.Cryptography; +using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Cryptography; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Events; +using MediaBrowser.Model.Users; +using Microsoft.Extensions.Logging; + +namespace Jellyfin.Server.Implementations.Users +{ + public class UserManager : IUserManager + { + private readonly JellyfinDbProvider _dbProvider; + private readonly ICryptoProvider _cryptoProvider; + private readonly INetworkManager _networkManager; + private readonly ILogger _logger; + + private IAuthenticationProvider[] _authenticationProviders; + private DefaultAuthenticationProvider _defaultAuthenticationProvider; + private InvalidAuthProvider _invalidAuthProvider; + private IPasswordResetProvider[] _passwordResetProviders; + private DefaultPasswordResetProvider _defaultPasswordResetProvider; + + public UserManager( + JellyfinDbProvider dbProvider, + ICryptoProvider cryptoProvider, + INetworkManager networkManager, + ILogger logger) + { + _dbProvider = dbProvider; + _cryptoProvider = cryptoProvider; + _networkManager = networkManager; + _logger = logger; + } + + public event EventHandler> OnUserPasswordChanged; + + /// + public event EventHandler> OnUserUpdated; + + /// + public event EventHandler> OnUserCreated; + + /// + public event EventHandler> OnUserDeleted; + + public event EventHandler> OnUserLockedOut; + + public IEnumerable Users + { + get + { + var dbContext = _dbProvider.CreateContext(); + return dbContext.Users; + } + } + + public IEnumerable UsersIds + { + get + { + var dbContext = _dbProvider.CreateContext(); + return dbContext.Users.Select(u => u.Id); + } + } + + public User GetUserById(Guid id) + { + if (id == Guid.Empty) + { + throw new ArgumentException("Guid can't be empty", nameof(id)); + } + + var dbContext = _dbProvider.CreateContext(); + + return dbContext.Users.Find(id); + } + + public User GetUserByName(string name) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("Invalid username", nameof(name)); + } + + var dbContext = _dbProvider.CreateContext(); + + // This can't use an overload with StringComparer because that would cause the query to + // have to be evaluated client-side. + return dbContext.Users.FirstOrDefault(u => string.Equals(u.Username, name)); + } + + public async Task RenameUser(User user, string newName) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + if (string.IsNullOrWhiteSpace(newName)) + { + throw new ArgumentException("Invalid username", nameof(newName)); + } + + if (user.Username.Equals(newName, StringComparison.Ordinal)) + { + throw new ArgumentException("The new and old names must be different."); + } + + if (Users.Any( + u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase))) + { + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "A user with the name '{0}' already exists.", + newName)); + } + + user.Username = newName; + await UpdateUserAsync(user).ConfigureAwait(false); + + OnUserUpdated?.Invoke(this, new GenericEventArgs(user)); + } + + public void UpdateUser(User user) + { + var dbContext = _dbProvider.CreateContext(); + dbContext.Users.Update(user); + dbContext.SaveChanges(); + } + + public async Task UpdateUserAsync(User user) + { + var dbContext = _dbProvider.CreateContext(); + dbContext.Users.Update(user); + + await dbContext.SaveChangesAsync().ConfigureAwait(false); + } + + public User CreateUser(string name) + { + if (!IsValidUsername(name)) + { + throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)"); + } + + var dbContext = _dbProvider.CreateContext(); + + var newUser = new User(name, _defaultAuthenticationProvider.GetType().FullName); + dbContext.Users.Add(newUser); + dbContext.SaveChanges(); + + OnUserCreated?.Invoke(this, new GenericEventArgs(newUser)); + + return newUser; + } + + public void DeleteUser(User user) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + var dbContext = _dbProvider.CreateContext(); + + if (!dbContext.Users.Contains(user)) + { + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "The user cannot be deleted because there is no user with the Name {0} and Id {1}.", + user.Username, + user.Id)); + } + + if (dbContext.Users.Count() == 1) + { + throw new InvalidOperationException(string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one user in the system.", + user.Username)); + } + + if (user.HasPermission(PermissionKind.IsAdministrator) + && Users.Count(i => i.HasPermission(PermissionKind.IsAdministrator)) == 1) + { + throw new ArgumentException( + string.Format( + CultureInfo.InvariantCulture, + "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", + user.Username), + nameof(user)); + } + + dbContext.Users.Remove(user); + dbContext.SaveChanges(); + OnUserDeleted?.Invoke(this, new GenericEventArgs(user)); + } + + public Task ResetPassword(User user) + { + return ChangePassword(user, string.Empty); + } + + public void ResetEasyPassword(User user) + { + ChangeEasyPassword(user, string.Empty, null); + } + + public async Task ChangePassword(User user, string newPassword) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false); + await UpdateUserAsync(user).ConfigureAwait(false); + + OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); + } + + public void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1) + { + GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1); + UpdateUser(user); + + OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); + } + + public UserDto GetUserDto(User user, string remoteEndPoint = null) + { + return new UserDto + { + Id = user.Id, + HasPassword = user.Password == null, + EnableAutoLogin = user.EnableAutoLogin, + LastLoginDate = user.LastLoginDate, + LastActivityDate = user.LastActivityDate, + Configuration = new UserConfiguration + { + SubtitleMode = user.SubtitleMode, + HidePlayedInLatest = user.HidePlayedInLatest, + EnableLocalPassword = user.EnableLocalPassword, + PlayDefaultAudioTrack = user.PlayDefaultAudioTrack, + DisplayCollectionsView = user.DisplayCollectionsView, + DisplayMissingEpisodes = user.DisplayMissingEpisodes, + AudioLanguagePreference = user.AudioLanguagePreference, + RememberAudioSelections = user.RememberAudioSelections, + EnableNextEpisodeAutoPlay = user.EnableNextEpisodeAutoPlay, + RememberSubtitleSelections = user.RememberSubtitleSelections, + SubtitleLanguagePreference = user.SubtitleLanguagePreference, + OrderedViews = user.GetPreference(PreferenceKind.OrderedViews), + GroupedFolders = user.GetPreference(PreferenceKind.GroupedFolders), + MyMediaExcludes = user.GetPreference(PreferenceKind.MyMediaExcludes), + LatestItemsExcludes = user.GetPreference(PreferenceKind.LatestItemExcludes) + }, + Policy = new UserPolicy + { + MaxParentalRating = user.MaxParentalAgeRating, + EnableUserPreferenceAccess = user.EnableUserPreferenceAccess, + RemoteClientBitrateLimit = user.RemoteClientBitrateLimit.GetValueOrDefault(), + AuthenticationProviderId = user.AuthenticationProviderId, + PasswordResetProviderId = user.PasswordResetProviderId, + InvalidLoginAttemptCount = user.InvalidLoginAttemptCount, + LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout.GetValueOrDefault(), + IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator), + IsHidden = user.HasPermission(PermissionKind.IsHidden), + IsDisabled = user.HasPermission(PermissionKind.IsDisabled), + EnableSharedDeviceControl = user.HasPermission(PermissionKind.EnableSharedDeviceControl), + EnableRemoteAccess = user.HasPermission(PermissionKind.EnableRemoteAccess), + EnableLiveTvManagement = user.HasPermission(PermissionKind.EnableLiveTvManagement), + EnableLiveTvAccess = user.HasPermission(PermissionKind.EnableLiveTvAccess), + EnableMediaPlayback = user.HasPermission(PermissionKind.EnableMediaPlayback), + EnableAudioPlaybackTranscoding = user.HasPermission(PermissionKind.EnableAudioPlaybackTranscoding), + EnableVideoPlaybackTranscoding = user.HasPermission(PermissionKind.EnableVideoPlaybackTranscoding), + EnableContentDeletion = user.HasPermission(PermissionKind.EnableContentDeletion), + EnableContentDownloading = user.HasPermission(PermissionKind.EnableContentDownloading), + EnableSyncTranscoding = user.HasPermission(PermissionKind.EnableSyncTranscoding), + EnableMediaConversion = user.HasPermission(PermissionKind.EnableMediaConversion), + EnableAllChannels = user.HasPermission(PermissionKind.EnableAllChannels), + EnableAllDevices = user.HasPermission(PermissionKind.EnableAllDevices), + EnableAllFolders = user.HasPermission(PermissionKind.EnableAllFolders), + EnableRemoteControlOfOtherUsers = user.HasPermission(PermissionKind.EnableRemoteControlOfOtherUsers), + EnablePlaybackRemuxing = user.HasPermission(PermissionKind.EnablePlaybackRemuxing), + ForceRemoteSourceTranscoding = user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding), + EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing), + AccessSchedules = user.AccessSchedules.ToArray(), + BlockedTags = user.GetPreference(PreferenceKind.BlockedTags), + EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels), + EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices), + EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders), + EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders) + } + }; + } + + public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null) + { + if (user == null) + { + throw new ArgumentNullException(nameof(user)); + } + + bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user); + bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user)); + + bool hasPassword = user.EnableLocalPassword && + !string.IsNullOrEmpty(remoteEndPoint) && + _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword; + + return new PublicUserDto + { + Name = user.Username, + HasPassword = hasPassword, + HasConfiguredPassword = hasConfiguredPassword + }; + } + + public async Task AuthenticateUser( + string username, + string password, + string passwordSha1, + string remoteEndPoint, + bool isUserSession) + { + if (string.IsNullOrWhiteSpace(username)) + { + _logger.LogInformation("Authentication request without username has been denied (IP: {IP}).", remoteEndPoint); + throw new ArgumentNullException(nameof(username)); + } + + var user = Users.ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); + bool success; + IAuthenticationProvider authenticationProvider; + + if (user != null) + { + var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint) + .ConfigureAwait(false); + authenticationProvider = authResult.authenticationProvider; + success = authResult.success; + } + else + { + var authResult = await AuthenticateLocalUser(username, password, null, remoteEndPoint) + .ConfigureAwait(false); + authenticationProvider = authResult.authenticationProvider; + string updatedUsername = authResult.username; + success = authResult.success; + + if (success + && authenticationProvider != null + && !(authenticationProvider is DefaultAuthenticationProvider)) + { + // Trust the username returned by the authentication provider + username = updatedUsername; + + // Search the database for the user again + // the authentication provider might have created it + user = Users + .ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); + + if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) + { + UpdatePolicy(user.Id, hasNewUserPolicy.GetNewUserPolicy()); + + await UpdateUserAsync(user).ConfigureAwait(false); + } + } + } + + if (success && user != null && authenticationProvider != null) + { + var providerId = authenticationProvider.GetType().FullName; + + if (!string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase)) + { + user.AuthenticationProviderId = providerId; + await UpdateUserAsync(user).ConfigureAwait(false); + } + } + + if (user == null) + { + _logger.LogInformation( + "Authentication request for {UserName} has been denied (IP: {IP}).", + username, + remoteEndPoint); + throw new AuthenticationException("Invalid username or password entered."); + } + + if (user.HasPermission(PermissionKind.IsDisabled)) + { + _logger.LogInformation( + "Authentication request for {UserName} has been denied because this account is currently disabled (IP: {IP}).", + username, + remoteEndPoint); + throw new SecurityException( + $"The {user.Username} account is currently disabled. Please consult with your administrator."); + } + + if (!user.HasPermission(PermissionKind.EnableRemoteAccess) && + !_networkManager.IsInLocalNetwork(remoteEndPoint)) + { + _logger.LogInformation( + "Authentication request for {UserName} forbidden: remote access disabled and user not in local network (IP: {IP}).", + username, + remoteEndPoint); + throw new SecurityException("Forbidden."); + } + + if (!user.IsParentalScheduleAllowed()) + { + _logger.LogInformation( + "Authentication request for {UserName} is not allowed at this time due parental restrictions (IP: {IP}).", + username, + remoteEndPoint); + throw new SecurityException("User is not allowed access at this time."); + } + + // Update LastActivityDate and LastLoginDate, then save + if (success) + { + if (isUserSession) + { + user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow; + await UpdateUserAsync(user).ConfigureAwait(false); + } + + user.InvalidLoginAttemptCount = 0; + _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Username); + } + else + { + IncrementInvalidLoginAttemptCount(user); + _logger.LogInformation( + "Authentication request for {UserName} has been denied (IP: {IP}).", + user.Username, + remoteEndPoint); + } + + return success ? user : null; + } + + public async Task StartForgotPasswordProcess(string enteredUsername, bool isInNetwork) + { + var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername); + + var action = ForgotPasswordAction.InNetworkRequired; + + if (user != null && isInNetwork) + { + var passwordResetProvider = GetPasswordResetProvider(user); + return await passwordResetProvider.StartForgotPasswordProcess(user, isInNetwork).ConfigureAwait(false); + } + + return new ForgotPasswordResult + { + Action = action, + PinFile = string.Empty + }; + } + + public async Task RedeemPasswordResetPin(string pin) + { + foreach (var provider in _passwordResetProviders) + { + var result = await provider.RedeemPasswordResetPin(pin).ConfigureAwait(false); + + if (result.Success) + { + return result; + } + } + + return new PinRedeemResult + { + Success = false, + UsersReset = Array.Empty() + }; + } + + public void AddParts(IEnumerable authenticationProviders, IEnumerable passwordResetProviders) + { + _authenticationProviders = authenticationProviders.ToArray(); + _passwordResetProviders = passwordResetProviders.ToArray(); + + _invalidAuthProvider = _authenticationProviders.OfType().First(); + _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); + _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); + } + + public NameIdPair[] GetAuthenticationProviders() + { + return _authenticationProviders + .Where(provider => provider.IsEnabled) + .OrderBy(i => i is DefaultAuthenticationProvider ? 0 : 1) + .ThenBy(i => i.Name) + .Select(i => new NameIdPair + { + Name = i.Name, + Id = i.GetType().FullName + }) + .ToArray(); + } + + public NameIdPair[] GetPasswordResetProviders() + { + return _passwordResetProviders + .Where(provider => provider.IsEnabled) + .OrderBy(i => i is DefaultPasswordResetProvider ? 0 : 1) + .ThenBy(i => i.Name) + .Select(i => new NameIdPair + { + Name = i.Name, + Id = i.GetType().FullName + }) + .ToArray(); + } + + public void UpdateConfiguration(Guid userId, UserConfiguration config) + { + var user = GetUserById(userId); + user.SubtitleMode = config.SubtitleMode; + user.HidePlayedInLatest = config.HidePlayedInLatest; + user.EnableLocalPassword = config.EnableLocalPassword; + user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack; + user.DisplayCollectionsView = config.DisplayCollectionsView; + user.DisplayMissingEpisodes = config.DisplayMissingEpisodes; + user.AudioLanguagePreference = config.AudioLanguagePreference; + user.RememberAudioSelections = config.RememberAudioSelections; + user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay; + user.RememberSubtitleSelections = config.RememberSubtitleSelections; + user.SubtitleLanguagePreference = config.SubtitleLanguagePreference; + + user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews); + user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders); + user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes); + user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); + + UpdateUser(user); + } + + public void UpdatePolicy(Guid userId, UserPolicy policy) + { + var user = GetUserById(userId); + + user.MaxParentalAgeRating = policy.MaxParentalRating; + user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess; + user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit; + user.AuthenticationProviderId = policy.AuthenticationProviderId; + user.PasswordResetProviderId = policy.PasswordResetProviderId; + user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; + user.LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1 + ? null + : new int?(policy.LoginAttemptsBeforeLockout); + user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); + user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); + user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled); + user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl); + user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess); + user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement); + user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess); + user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback); + user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding); + user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding); + user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion); + user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading); + user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding); + user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion); + user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels); + user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices); + user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders); + user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers); + user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing); + user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding); + user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing); + + user.AccessSchedules.Clear(); + foreach (var policyAccessSchedule in policy.AccessSchedules) + { + user.AccessSchedules.Add(policyAccessSchedule); + } + + user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); + user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); + user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); + user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); + user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); + } + + private bool IsValidUsername(string name) + { + // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ + // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness + // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.) + return Regex.IsMatch(name, @"^[\w\-'._@]*$"); + } + + private IAuthenticationProvider GetAuthenticationProvider(User user) + { + return GetAuthenticationProviders(user)[0]; + } + + private IPasswordResetProvider GetPasswordResetProvider(User user) + { + return GetPasswordResetProviders(user)[0]; + } + + private IList GetAuthenticationProviders(User user) + { + var authenticationProviderId = user?.AuthenticationProviderId; + + var providers = _authenticationProviders.Where(i => i.IsEnabled).ToList(); + + if (!string.IsNullOrEmpty(authenticationProviderId)) + { + providers = providers.Where(i => string.Equals(authenticationProviderId, i.GetType().FullName, StringComparison.OrdinalIgnoreCase)).ToList(); + } + + if (providers.Count == 0) + { + // Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found + _logger.LogWarning( + "User {Username} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected", + user?.Username, + user?.AuthenticationProviderId); + providers = new List + { + _invalidAuthProvider + }; + } + + return providers; + } + + private IList GetPasswordResetProviders(User user) + { + var passwordResetProviderId = user?.PasswordResetProviderId; + var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray(); + + if (!string.IsNullOrEmpty(passwordResetProviderId)) + { + providers = providers.Where(i => + string.Equals(passwordResetProviderId, i.GetType().FullName, StringComparison.OrdinalIgnoreCase)) + .ToArray(); + } + + if (providers.Length == 0) + { + providers = new IPasswordResetProvider[] + { + _defaultPasswordResetProvider + }; + } + + return providers; + } + + private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser( + string username, + string password, + User user, + string remoteEndPoint) + { + bool success = false; + IAuthenticationProvider authenticationProvider = null; + + foreach (var provider in GetAuthenticationProviders(user)) + { + var providerAuthResult = + await AuthenticateWithProvider(provider, username, password, user).ConfigureAwait(false); + var updatedUsername = providerAuthResult.username; + success = providerAuthResult.success; + + if (success) + { + authenticationProvider = provider; + username = updatedUsername; + break; + } + } + + if (!success + && _networkManager.IsInLocalNetwork(remoteEndPoint) + && user?.EnableLocalPassword == true + && !string.IsNullOrEmpty(user.EasyPassword)) + { + // Check easy password + var passwordHash = PasswordHash.Parse(user.EasyPassword); + var hash = _cryptoProvider.ComputeHash( + passwordHash.Id, + Encoding.UTF8.GetBytes(password), + passwordHash.Salt.ToArray()); + success = passwordHash.Hash.SequenceEqual(hash); + } + + return (authenticationProvider, username, success); + } + + private async Task<(string username, bool success)> AuthenticateWithProvider( + IAuthenticationProvider provider, + string username, + string password, + User resolvedUser) + { + try + { + var authenticationResult = provider is IRequiresResolvedUser requiresResolvedUser + ? await requiresResolvedUser.Authenticate(username, password, resolvedUser).ConfigureAwait(false) + : await provider.Authenticate(username, password).ConfigureAwait(false); + + if (authenticationResult.Username != username) + { + _logger.LogDebug("Authentication provider provided updated username {1}", authenticationResult.Username); + username = authenticationResult.Username; + } + + return (username, true); + } + catch (AuthenticationException ex) + { + _logger.LogError(ex, "Error authenticating with provider {Provider}", provider.Name); + + return (username, false); + } + } + + private void IncrementInvalidLoginAttemptCount(User user) + { + int invalidLogins = user.InvalidLoginAttemptCount; + int? maxInvalidLogins = user.LoginAttemptsBeforeLockout; + if (maxInvalidLogins.HasValue && invalidLogins >= maxInvalidLogins) + { + user.SetPermission(PermissionKind.IsDisabled, true); + OnUserLockedOut?.Invoke(this, new GenericEventArgs(user)); + _logger.LogWarning( + "Disabling user {Username} due to {Attempts} unsuccessful login attempts.", + user.Username, + invalidLogins); + } + + UpdateUser(user); + } + } +} -- cgit v1.2.3 From e7b297c67baee73a737c61170ad6a55c2f1d08d3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 19 May 2020 15:58:25 -0400 Subject: Add some missing properties --- Jellyfin.Server.Implementations/Users/UserManager.cs | 16 +++++++++++++--- Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 1 - 2 files changed, 13 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index ddc05055b..2d0caee29 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -10,9 +10,11 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; +using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; @@ -29,6 +31,8 @@ namespace Jellyfin.Server.Implementations.Users private readonly JellyfinDbProvider _dbProvider; private readonly ICryptoProvider _cryptoProvider; private readonly INetworkManager _networkManager; + private readonly IApplicationHost _appHost; + private readonly IImageProcessor _imageProcessor; private readonly ILogger _logger; private IAuthenticationProvider[] _authenticationProviders; @@ -41,11 +45,15 @@ namespace Jellyfin.Server.Implementations.Users JellyfinDbProvider dbProvider, ICryptoProvider cryptoProvider, INetworkManager networkManager, + IApplicationHost appHost, + IImageProcessor imageProcessor, ILogger logger) { _dbProvider = dbProvider; _cryptoProvider = cryptoProvider; _networkManager = networkManager; + _appHost = appHost; + _imageProcessor = imageProcessor; _logger = logger; } @@ -123,8 +131,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("The new and old names must be different."); } - if (Users.Any( - u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase))) + if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, @@ -248,11 +255,14 @@ namespace Jellyfin.Server.Implementations.Users { return new UserDto { + Name = user.Username, Id = user.Id, + ServerId = _appHost.SystemId, HasPassword = user.Password == null, EnableAutoLogin = user.EnableAutoLogin, LastLoginDate = user.LastLoginDate, LastActivityDate = user.LastActivityDate, + PrimaryImageTag = user.ProfileImage != null ? _imageProcessor.GetImageCacheTag(user) : null, Configuration = new UserConfiguration { SubtitleMode = user.SubtitleMode, @@ -265,7 +275,7 @@ namespace Jellyfin.Server.Implementations.Users RememberAudioSelections = user.RememberAudioSelections, EnableNextEpisodeAutoPlay = user.EnableNextEpisodeAutoPlay, RememberSubtitleSelections = user.RememberSubtitleSelections, - SubtitleLanguagePreference = user.SubtitleLanguagePreference, + SubtitleLanguagePreference = user.SubtitleLanguagePreference ?? string.Empty, OrderedViews = user.GetPreference(PreferenceKind.OrderedViews), GroupedFolders = user.GetPreference(PreferenceKind.GroupedFolders), MyMediaExcludes = user.GetPreference(PreferenceKind.MyMediaExcludes), diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 0271098f4..9a428a747 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -9,7 +9,6 @@ using Jellyfin.Server.Implementations.Users; using MediaBrowser.Controller; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Users; -using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using SQLitePCL.pretty; using JsonSerializer = System.Text.Json.JsonSerializer; -- cgit v1.2.3 From 292993d8efabf0380d7b6a53e074324b4c92f377 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 19 May 2020 19:44:55 -0400 Subject: Document various classes. --- Jellyfin.Data/Entities/Preference.cs | 23 ++++-- Jellyfin.Data/Enums/PermissionKind.cs | 86 ++++++++++++++++++++++ Jellyfin.Data/Enums/PreferenceKind.cs | 50 +++++++++++++ .../Users/UserManager.cs | 38 +++++++++- 4 files changed, 190 insertions(+), 7 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 56a07d440..0ca9d7eff 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -35,30 +35,42 @@ namespace Jellyfin.Data.Entities *************************************************************************/ /// - /// Identity, Indexed, Required + /// Gets or sets the id of this preference. /// + /// + /// Identity, Indexed, Required. + /// [Key] [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } /// - /// Required + /// Gets or sets the type of this preference. /// + /// + /// Required. + /// [Required] - public PreferenceKind Kind { get; set; } + public PreferenceKind Kind { get; protected set; } /// - /// Required, Max length = 65535 + /// Gets or sets the value of this preference. /// + /// + /// Required, Max length = 65535. + /// [Required] [MaxLength(65535)] [StringLength(65535)] public string Value { get; set; } /// - /// Required, ConcurrencyToken. + /// Gets or sets the row version. /// + /// + /// Required, ConcurrencyToken. + /// [ConcurrencyCheck] [Required] public uint RowVersion { get; set; } @@ -81,4 +93,3 @@ namespace Jellyfin.Data.Entities } } } - diff --git a/Jellyfin.Data/Enums/PermissionKind.cs b/Jellyfin.Data/Enums/PermissionKind.cs index df18261e6..8b1472f97 100644 --- a/Jellyfin.Data/Enums/PermissionKind.cs +++ b/Jellyfin.Data/Enums/PermissionKind.cs @@ -1,27 +1,113 @@ namespace Jellyfin.Data.Enums { + /// + /// The types of user permissions. + /// public enum PermissionKind { + /// + /// Whether the user is an administrator. + /// IsAdministrator, + + /// + /// Whether the user is hidden. + /// IsHidden, + + /// + /// Whether the user is disabled. + /// IsDisabled, + + /// + /// Whether the user can control shared devices. + /// EnableSharedDeviceControl, + + /// + /// Whether the user can access the server remotely. + /// EnableRemoteAccess, + + /// + /// Whether the user can manage live tv. + /// EnableLiveTvManagement, + + /// + /// Whether the user can access live tv. + /// EnableLiveTvAccess, + + /// + /// Whether the user can play media. + /// EnableMediaPlayback, + + /// + /// Whether the server should transcode audio for the user if requested. + /// EnableAudioPlaybackTranscoding, + + /// + /// Whether the server should transcode video for the user if requested. + /// EnableVideoPlaybackTranscoding, + + /// + /// Whether the user can delete content. + /// EnableContentDeletion, + + /// + /// Whether the user can download content. + /// EnableContentDownloading, + + /// + /// Whether to enable sync transcoding for the user. + /// EnableSyncTranscoding, + + /// + /// Whether the user can do media conversion. + /// EnableMediaConversion, + + /// + /// Whether the user has access to all devices. + /// EnableAllDevices, + + /// + /// Whether the user has access to all channels. + /// EnableAllChannels, + + /// + /// Whether the user has access to all folders. + /// EnableAllFolders, + + /// + /// Whether to enable public sharing for the user. + /// EnablePublicSharing, + + /// + /// Whether the user can remotely control other users. + /// EnableRemoteControlOfOtherUsers, + + /// + /// Whether the user is permitted to do playback remuxing. + /// EnablePlaybackRemuxing, + + /// + /// Whether the server should force transcoding on remote connections for the user. + /// ForceRemoteSourceTranscoding } } diff --git a/Jellyfin.Data/Enums/PreferenceKind.cs b/Jellyfin.Data/Enums/PreferenceKind.cs index ea1221e1a..e0e9cfe04 100644 --- a/Jellyfin.Data/Enums/PreferenceKind.cs +++ b/Jellyfin.Data/Enums/PreferenceKind.cs @@ -1,18 +1,68 @@ namespace Jellyfin.Data.Enums { + /// + /// The types of user preferences. + /// public enum PreferenceKind { + /// + /// A list of blocked tags. + /// BlockedTags, + + /// + /// A list of blocked channels. + /// BlockedChannels, + + /// + /// A list of blocked media folders. + /// BlockedMediaFolders, + + /// + /// A list of enabled devices. + /// EnabledDevices, + + /// + /// A list of enabled channels + /// EnabledChannels, + + /// + /// A list of enabled folders. + /// EnabledFolders, + + /// + /// A list of folders to allow content deletion from. + /// EnableContentDeletionFromFolders, + + /// + /// A list of latest items to exclude. + /// LatestItemExcludes, + + /// + /// A list of media to exclude. + /// MyMediaExcludes, + + /// + /// A list of grouped folders. + /// GroupedFolders, + + /// + /// A list of unrated items to block. + /// BlockUnratedItems, + + /// + /// A list of ordered views. + /// OrderedViews } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 2d0caee29..291574155 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -1,5 +1,4 @@ #pragma warning disable CA1307 -#pragma warning disable CS1591 using System; using System.Collections.Generic; @@ -26,6 +25,9 @@ using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Implementations.Users { + /// + /// Manages the creation and retrieval of instances. + /// public class UserManager : IUserManager { private readonly JellyfinDbProvider _dbProvider; @@ -41,6 +43,15 @@ namespace Jellyfin.Server.Implementations.Users private IPasswordResetProvider[] _passwordResetProviders; private DefaultPasswordResetProvider _defaultPasswordResetProvider; + /// + /// Initializes a new instance of the class. + /// + /// The database provider. + /// The cryptography provider. + /// The network manager. + /// The application host. + /// The image processor. + /// The logger. public UserManager( JellyfinDbProvider dbProvider, ICryptoProvider cryptoProvider, @@ -57,6 +68,7 @@ namespace Jellyfin.Server.Implementations.Users _logger = logger; } + /// public event EventHandler> OnUserPasswordChanged; /// @@ -68,8 +80,10 @@ namespace Jellyfin.Server.Implementations.Users /// public event EventHandler> OnUserDeleted; + /// public event EventHandler> OnUserLockedOut; + /// public IEnumerable Users { get @@ -79,6 +93,7 @@ namespace Jellyfin.Server.Implementations.Users } } + /// public IEnumerable UsersIds { get @@ -88,6 +103,7 @@ namespace Jellyfin.Server.Implementations.Users } } + /// public User GetUserById(Guid id) { if (id == Guid.Empty) @@ -100,6 +116,7 @@ namespace Jellyfin.Server.Implementations.Users return dbContext.Users.Find(id); } + /// public User GetUserByName(string name) { if (string.IsNullOrWhiteSpace(name)) @@ -114,6 +131,7 @@ namespace Jellyfin.Server.Implementations.Users return dbContext.Users.FirstOrDefault(u => string.Equals(u.Username, name)); } + /// public async Task RenameUser(User user, string newName) { if (user == null) @@ -145,6 +163,7 @@ namespace Jellyfin.Server.Implementations.Users OnUserUpdated?.Invoke(this, new GenericEventArgs(user)); } + /// public void UpdateUser(User user) { var dbContext = _dbProvider.CreateContext(); @@ -152,6 +171,7 @@ namespace Jellyfin.Server.Implementations.Users dbContext.SaveChanges(); } + /// public async Task UpdateUserAsync(User user) { var dbContext = _dbProvider.CreateContext(); @@ -160,6 +180,7 @@ namespace Jellyfin.Server.Implementations.Users await dbContext.SaveChangesAsync().ConfigureAwait(false); } + /// public User CreateUser(string name) { if (!IsValidUsername(name)) @@ -178,6 +199,7 @@ namespace Jellyfin.Server.Implementations.Users return newUser; } + /// public void DeleteUser(User user) { if (user == null) @@ -220,16 +242,19 @@ namespace Jellyfin.Server.Implementations.Users OnUserDeleted?.Invoke(this, new GenericEventArgs(user)); } + /// public Task ResetPassword(User user) { return ChangePassword(user, string.Empty); } + /// public void ResetEasyPassword(User user) { ChangeEasyPassword(user, string.Empty, null); } + /// public async Task ChangePassword(User user, string newPassword) { if (user == null) @@ -243,6 +268,7 @@ namespace Jellyfin.Server.Implementations.Users OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); } + /// public void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1) { GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1); @@ -251,6 +277,7 @@ namespace Jellyfin.Server.Implementations.Users OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); } + /// public UserDto GetUserDto(User user, string remoteEndPoint = null) { return new UserDto @@ -321,6 +348,7 @@ namespace Jellyfin.Server.Implementations.Users }; } + /// public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null) { if (user == null) @@ -343,6 +371,7 @@ namespace Jellyfin.Server.Implementations.Users }; } + /// public async Task AuthenticateUser( string username, string password, @@ -469,6 +498,7 @@ namespace Jellyfin.Server.Implementations.Users return success ? user : null; } + /// public async Task StartForgotPasswordProcess(string enteredUsername, bool isInNetwork) { var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername); @@ -488,6 +518,7 @@ namespace Jellyfin.Server.Implementations.Users }; } + /// public async Task RedeemPasswordResetPin(string pin) { foreach (var provider in _passwordResetProviders) @@ -507,6 +538,7 @@ namespace Jellyfin.Server.Implementations.Users }; } + /// public void AddParts(IEnumerable authenticationProviders, IEnumerable passwordResetProviders) { _authenticationProviders = authenticationProviders.ToArray(); @@ -517,6 +549,7 @@ namespace Jellyfin.Server.Implementations.Users _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); } + /// public NameIdPair[] GetAuthenticationProviders() { return _authenticationProviders @@ -531,6 +564,7 @@ namespace Jellyfin.Server.Implementations.Users .ToArray(); } + /// public NameIdPair[] GetPasswordResetProviders() { return _passwordResetProviders @@ -545,6 +579,7 @@ namespace Jellyfin.Server.Implementations.Users .ToArray(); } + /// public void UpdateConfiguration(Guid userId, UserConfiguration config) { var user = GetUserById(userId); @@ -568,6 +603,7 @@ namespace Jellyfin.Server.Implementations.Users UpdateUser(user); } + /// public void UpdatePolicy(Guid userId, UserPolicy policy) { var user = GetUserById(userId); -- cgit v1.2.3 From 1d1a145ad4386b39b983d539001abfccf5f53b23 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 19 May 2020 22:12:03 -0400 Subject: Fix issues and add profile image support --- Jellyfin.Data/Entities/ImageInfo.cs | 13 +++++++++++-- Jellyfin.Data/Entities/Permission.cs | 18 ++++++++++-------- Jellyfin.Data/Entities/User.cs | 8 +++++--- Jellyfin.Server.Implementations/Users/UserManager.cs | 5 ++++- Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 17 +++++++++++++++-- MediaBrowser.Api/Images/ImageService.cs | 3 ++- MediaBrowser.Providers/Manager/ImageSaver.cs | 9 ++------- MediaBrowser.Providers/Manager/ProviderManager.cs | 2 +- 8 files changed, 50 insertions(+), 25 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index 336c13b36..659369545 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -1,24 +1,33 @@ using System; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace Jellyfin.Data.Entities { public class ImageInfo { - public ImageInfo(string path) + public ImageInfo(string path, int width, int height) { Path = path; + Width = width; + Height = height; LastModified = DateTime.UtcNow; } [Key] [Required] - + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } [Required] public string Path { get; set; } + [Required] + public int Width { get; set; } + + [Required] + public int Height { get; set; } + [Required] public DateTime LastModified { get; set; } } diff --git a/Jellyfin.Data/Entities/Permission.cs b/Jellyfin.Data/Entities/Permission.cs index 136e7abd3..706128028 100644 --- a/Jellyfin.Data/Entities/Permission.cs +++ b/Jellyfin.Data/Entities/Permission.cs @@ -12,14 +12,7 @@ namespace Jellyfin.Data.Entities partial void Init(); /// - /// Default constructor. Protected due to required properties, but present because EF needs it. - /// - protected Permission() - { - Init(); - } - - /// + /// Initializes a new instance of the class. /// Public constructor with required data /// /// @@ -33,6 +26,15 @@ namespace Jellyfin.Data.Entities Init(); } + /// + /// Initializes a new instance of the class. + /// Default constructor. Protected due to required properties, but present because EF needs it. + /// + protected Permission() + { + Init(); + } + /// /// Static create function (for use in LINQ queries, etc.) /// diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index bd1cde31c..783823a17 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -25,7 +25,7 @@ namespace Jellyfin.Data.Entities /// /// The username for the new user. /// The authentication provider's Id - public User(string username, string authenticationProviderId) + public User(string username, string authenticationProviderId, string passwordResetProviderId) { if (string.IsNullOrEmpty(username)) { @@ -39,6 +39,7 @@ namespace Jellyfin.Data.Entities Username = username; AuthenticationProviderId = authenticationProviderId; + PasswordResetProviderId = passwordResetProviderId; Groups = new HashSet(); Permissions = new HashSet(); @@ -85,10 +86,11 @@ namespace Jellyfin.Data.Entities /// /// The username for the created user. /// The Id of the user's authentication provider. + /// The Id of the user's password reset provider. /// The created instance. - public static User Create(string username, string authenticationProviderId) + public static User Create(string username, string authenticationProviderId, string passwordResetProviderId) { - return new User(username, authenticationProviderId); + return new User(username, authenticationProviderId, passwordResetProviderId); } /************************************************************************* diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 291574155..18616f75f 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -190,7 +190,10 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = _dbProvider.CreateContext(); - var newUser = new User(name, _defaultAuthenticationProvider.GetType().FullName); + var newUser = new User( + name, + _defaultAuthenticationProvider.GetType().FullName, + _defaultPasswordResetProvider.GetType().FullName); dbContext.Users.Add(newUser); dbContext.SaveChanges(); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 9a428a747..987c85f4c 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -7,6 +7,7 @@ using Jellyfin.Data.Enums; using Jellyfin.Server.Implementations; using Jellyfin.Server.Implementations.Users; using MediaBrowser.Controller; +using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Users; using Microsoft.Extensions.Logging; @@ -84,9 +85,9 @@ namespace Jellyfin.Server.Migrations.Routines StringComparison.Ordinal) ?? typeof(DefaultAuthenticationProvider).FullName; - policy.PasswordResetProviderId ??= typeof(DefaultPasswordResetProvider).FullName; + policy.PasswordResetProviderId = typeof(DefaultPasswordResetProvider).FullName; - var user = new User(mockup.Name, policy.AuthenticationProviderId) + var user = new User(mockup.Name, policy.AuthenticationProviderId, string.Empty) { Id = entry[1].ReadGuidFromBlob(), InternalId = entry[0].ToInt64(), @@ -113,6 +114,16 @@ namespace Jellyfin.Server.Migrations.Routines LastActivityDate = mockup.LastActivityDate ?? DateTime.MinValue }; + if (mockup.ImageInfos.Length > 0) + { + ItemImageInfo info = mockup.ImageInfos[0]; + + user.ProfileImage = new ImageInfo(info.Path, info.Width, info.Height) + { + LastModified = info.DateModified + }; + } + user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled); @@ -184,6 +195,8 @@ namespace Jellyfin.Server.Migrations.Routines public DateTime? LastActivityDate { get; set; } public string Name { get; set; } + + public ItemImageInfo[] ImageInfos { get; set; } } } } diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index 5bdf1618b..1392184df 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -17,6 +17,7 @@ using MediaBrowser.Model.Drawing; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; @@ -887,7 +888,7 @@ namespace MediaBrowser.Api.Images var userDataPath = Path.Combine(ServerConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username); await _providerManager - .SaveImage(user, memoryStream, mimeType, Path.Combine(userDataPath, _imageProcessor.GetImageCacheTag(user))) + .SaveImage(user, memoryStream, mimeType, Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType))) .ConfigureAwait(false); await _userManager.UpdateUserAsync(user); } diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 6bed38780..3c94f6215 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -131,7 +131,7 @@ namespace MediaBrowser.Providers.Manager var currentImage = GetCurrentImage(item, type, index); var currentImageIsLocalFile = currentImage != null && currentImage.IsLocalFile; - var currentImagePath = currentImage == null ? null : currentImage.Path; + var currentImagePath = currentImage?.Path; var savedPaths = new List(); @@ -179,13 +179,8 @@ namespace MediaBrowser.Providers.Manager } } - public async Task SaveImage(User user, Stream source, string mimeType, string path) + public async Task SaveImage(User user, Stream source, string path) { - if (string.IsNullOrEmpty(mimeType)) - { - throw new ArgumentNullException(nameof(mimeType)); - } - await SaveImageToLocation(source, path, path, CancellationToken.None).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index f4e875a24..be8fe3e1c 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -191,7 +191,7 @@ namespace MediaBrowser.Providers.Manager public Task SaveImage(User user, Stream source, string mimeType, string path) { return new ImageSaver(_configurationManager, _libraryMonitor, _fileSystem, _logger) - .SaveImage(user, source, mimeType, path); + .SaveImage(user, source, path); } public async Task> GetAvailableRemoteImages(BaseItem item, RemoteImageQuery query, CancellationToken cancellationToken) -- cgit v1.2.3 From 64c14beb27348daf0f440b5b1bc31ccb2987f9ff Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 20 May 2020 00:23:56 -0400 Subject: Fix default permissions and HasPassword property --- Jellyfin.Data/Entities/User.cs | 6 +++--- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 783823a17..52bbe8b18 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -325,10 +325,10 @@ namespace Jellyfin.Data.Entities { Permissions.Add(new Permission(PermissionKind.IsAdministrator, false)); Permissions.Add(new Permission(PermissionKind.IsDisabled, false)); - Permissions.Add(new Permission(PermissionKind.IsHidden, false)); - Permissions.Add(new Permission(PermissionKind.EnableAllChannels, false)); + Permissions.Add(new Permission(PermissionKind.IsHidden, true)); + Permissions.Add(new Permission(PermissionKind.EnableAllChannels, true)); Permissions.Add(new Permission(PermissionKind.EnableAllDevices, true)); - Permissions.Add(new Permission(PermissionKind.EnableAllFolders, false)); + Permissions.Add(new Permission(PermissionKind.EnableAllFolders, true)); Permissions.Add(new Permission(PermissionKind.EnableContentDeletion, false)); Permissions.Add(new Permission(PermissionKind.EnableContentDownloading, true)); Permissions.Add(new Permission(PermissionKind.EnableMediaConversion, true)); diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 18616f75f..4dd41792d 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -288,7 +288,7 @@ namespace Jellyfin.Server.Implementations.Users Name = user.Username, Id = user.Id, ServerId = _appHost.SystemId, - HasPassword = user.Password == null, + HasPassword = user.Password != null, EnableAutoLogin = user.EnableAutoLogin, LastLoginDate = user.LastLoginDate, LastActivityDate = user.LastActivityDate, -- cgit v1.2.3 From becfe018f0e3796bbd776068ea92d22daf483f2f Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 20 May 2020 13:49:44 -0400 Subject: Add internal id for new users --- Jellyfin.Server.Implementations/Users/UserManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 4dd41792d..599efe583 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -190,10 +190,16 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = _dbProvider.CreateContext(); + // Temporary measure until user item data is migrated. + var max = dbContext.Users.Select(u => u.InternalId).Max(); + var newUser = new User( name, _defaultAuthenticationProvider.GetType().FullName, - _defaultPasswordResetProvider.GetType().FullName); + _defaultPasswordResetProvider.GetType().FullName) + { + InternalId = max + 1 + }; dbContext.Users.Add(newUser); dbContext.SaveChanges(); -- cgit v1.2.3 From d72ea709955f17ad2034cd4c728d02cf3265e7e9 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 20 May 2020 19:47:41 -0400 Subject: Document user class and fix a few minor issues --- .../Devices/DeviceManager.cs | 9 +- Jellyfin.Data/Entities/User.cs | 219 ++++++++++++++++++--- .../Users/UserManager.cs | 6 +- 3 files changed, 194 insertions(+), 40 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index 2d44a7c28..a3c53035f 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -5,13 +5,11 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; -using System.Threading.Tasks; using Jellyfin.Data.Enums; using Jellyfin.Data.Entities; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; -using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Devices; @@ -19,7 +17,6 @@ using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; -using MediaBrowser.Model.Users; namespace Emby.Server.Implementations.Devices { @@ -30,11 +27,10 @@ namespace Emby.Server.Implementations.Devices private readonly IServerConfigurationManager _config; private readonly IAuthenticationRepository _authRepo; private readonly Dictionary _capabilitiesCache; + private readonly object _capabilitiesSyncLock = new object(); public event EventHandler>> DeviceOptionsUpdated; - private readonly object _capabilitiesSyncLock = new object(); - public DeviceManager( IAuthenticationRepository authRepo, IJsonSerializer json, @@ -184,8 +180,7 @@ namespace Emby.Server.Implementations.Devices throw new ArgumentNullException(nameof(deviceId)); } - if (user.HasPermission(PermissionKind.EnableAllDevices) - || user.HasPermission(PermissionKind.IsAdministrator)) + if (user.HasPermission(PermissionKind.EnableAllDevices) || user.HasPermission(PermissionKind.IsAdministrator)) { return true; } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index dd1dcfc6f..afda6169c 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -24,7 +24,8 @@ namespace Jellyfin.Data.Entities /// Public constructor with required data. /// /// The username for the new user. - /// The authentication provider's Id + /// The Id of the user's authentication provider. + /// The Id of the user's password reset provider. public User(string username, string authenticationProviderId, string passwordResetProviderId) { if (string.IsNullOrEmpty(username)) @@ -81,139 +82,234 @@ namespace Jellyfin.Data.Entities Init(); } - /// - /// Static create function (for use in LINQ queries, etc.) - /// - /// The username for the created user. - /// The Id of the user's authentication provider. - /// The Id of the user's password reset provider. - /// The created instance. - public static User Create(string username, string authenticationProviderId, string passwordResetProviderId) - { - return new User(username, authenticationProviderId, passwordResetProviderId); - } - /************************************************************************* * Properties *************************************************************************/ /// - /// Identity, Indexed, Required + /// Gets or sets the Id of the user. /// + /// + /// Identity, Indexed, Required. + /// [Key] [Required] [JsonIgnore] public Guid Id { get; set; } /// - /// Required, Max length = 255 + /// Gets or sets the user's name. /// + /// + /// Required, Max length = 255. + /// [Required] [MaxLength(255)] [StringLength(255)] public string Username { get; set; } /// - /// Max length = 65535 + /// Gets or sets the user's password, or null if none is set. /// + /// + /// Max length = 65535. + /// [MaxLength(65535)] [StringLength(65535)] public string Password { get; set; } /// - /// Max length = 65535. + /// Gets or sets the user's easy password, or null if none is set. /// + /// + /// Max length = 65535. + /// [MaxLength(65535)] [StringLength(65535)] public string EasyPassword { get; set; } /// - /// Required + /// Gets or sets a value indicating whether the user must update their password. /// + /// + /// Required. + /// [Required] public bool MustUpdatePassword { get; set; } /// - /// Max length = 255. + /// Gets or sets the audio language preference. /// + /// + /// Max length = 255. + /// [MaxLength(255)] [StringLength(255)] public string AudioLanguagePreference { get; set; } /// - /// Required, Max length = 255 + /// Gets or sets the authentication provider id. /// + /// + /// Required, Max length = 255. + /// [Required] [MaxLength(255)] [StringLength(255)] public string AuthenticationProviderId { get; set; } + /// + /// Gets or sets the password reset provider id. + /// + /// + /// Required, Max length = 255. + /// [Required] [MaxLength(255)] [StringLength(255)] public string PasswordResetProviderId { get; set; } /// - /// Required + /// Gets or sets the invalid login attempt count. /// + /// + /// Required. + /// [Required] public int InvalidLoginAttemptCount { get; set; } + /// + /// Gets or sets the last activity date. + /// public DateTime LastActivityDate { get; set; } + /// + /// Gets or sets the last login date. + /// public DateTime LastLoginDate { get; set; } + /// + /// Gets or sets the number of login attempts the user can make before they are locked out. + /// public int? LoginAttemptsBeforeLockout { get; set; } /// - /// Required. + /// Gets or sets the subtitle mode. /// + /// + /// Required. + /// [Required] public SubtitlePlaybackMode SubtitleMode { get; set; } /// - /// Required + /// Gets or sets a value indicating whether the default audio track should be played. /// + /// + /// Required. + /// [Required] public bool PlayDefaultAudioTrack { get; set; } /// /// Gets or sets the subtitle language preference. - /// Max length = 255 /// + /// + /// Max length = 255. + /// [MaxLength(255)] [StringLength(255)] public string SubtitleLanguagePreference { get; set; } + /// + /// Gets or sets a value indicating whether missing episodes should be displayed. + /// + /// + /// Required. + /// [Required] public bool DisplayMissingEpisodes { get; set; } + /// + /// Gets or sets a value indicating whether to display the collections view. + /// + /// + /// Required. + /// [Required] public bool DisplayCollectionsView { get; set; } + /// + /// Gets or sets a value indicating whether the user has a local password. + /// + /// + /// Required. + /// [Required] public bool EnableLocalPassword { get; set; } + /// + /// Gets or sets a value indicating whether the server should hide played content in "Latest". + /// + /// + /// Required. + /// [Required] public bool HidePlayedInLatest { get; set; } + /// + /// Gets or sets a value indicating whether to remember audio selections on played content. + /// + /// + /// Required. + /// [Required] public bool RememberAudioSelections { get; set; } + /// + /// Gets or sets a value indicating whether to remember subtitle selections on played content. + /// + /// + /// Required. + /// [Required] public bool RememberSubtitleSelections { get; set; } + /// + /// Gets or sets a value indicating whether to enable auto-play for the next episode. + /// + /// + /// Required. + /// [Required] public bool EnableNextEpisodeAutoPlay { get; set; } + /// + /// Gets or sets a value indicating whether the user should auto-login. + /// + /// + /// Required. + /// [Required] public bool EnableAutoLogin { get; set; } + /// + /// Gets or sets a value indicating whether the user can change their preferences. + /// + /// + /// Required. + /// [Required] public bool EnableUserPreferenceAccess { get; set; } + /// + /// Gets or sets the maximum parental age rating. + /// public int? MaxParentalAgeRating { get; set; } + /// + /// Gets or sets the remote client bitrate limit. + /// public int? RemoteClientBitrateLimit { get; set; } /// @@ -224,51 +320,100 @@ namespace Jellyfin.Data.Entities [Required] public long InternalId { get; set; } + /// + /// Gets or sets the user's profile image. Can be null. + /// public virtual ImageInfo ProfileImage { get; set; } /// /// Gets or sets the row version. - /// Required, ConcurrenyToken. /// + /// + /// Required, Concurrency Token. + /// [ConcurrencyCheck] [Required] public uint RowVersion { get; set; } - public void OnSavingChanges() - { - RowVersion++; - } - /************************************************************************* * Navigation properties *************************************************************************/ + + /// + /// Gets or sets the list of groups this user is a member of. + /// [ForeignKey("Group_Groups_Guid")] public virtual ICollection Groups { get; protected set; } + /// + /// Gets or sets the list of permissions this user has. + /// [ForeignKey("Permission_Permissions_Guid")] public virtual ICollection Permissions { get; protected set; } + /// + /// Gets or sets the list of provider mappings this user has. + /// [ForeignKey("ProviderMapping_ProviderMappings_Id")] public virtual ICollection ProviderMappings { get; protected set; } + /// + /// Gets or sets the list of preferences this user has. + /// [ForeignKey("Preference_Preferences_Guid")] public virtual ICollection Preferences { get; protected set; } + /// + /// Gets or sets the list of access schedules this user has. + /// public virtual ICollection AccessSchedules { get; protected set; } + /// + /// Static create function (for use in LINQ queries, etc.) + /// + /// The username for the created user. + /// The Id of the user's authentication provider. + /// The Id of the user's password reset provider. + /// The created instance. + public static User Create(string username, string authenticationProviderId, string passwordResetProviderId) + { + return new User(username, authenticationProviderId, passwordResetProviderId); + } + + /// + public void OnSavingChanges() + { + RowVersion++; + } + partial void Init(); + /// + /// Checks whether the user has the specified permission. + /// + /// The permission kind. + /// True if the user has the specified permission. public bool HasPermission(PermissionKind permission) { return Permissions.First(p => p.Kind == permission).Value; } + /// + /// Sets the given permission kind to the provided value. + /// + /// The permission kind. + /// The value to set. public void SetPermission(PermissionKind kind, bool value) { var permissionObj = Permissions.First(p => p.Kind == kind); permissionObj.Value = value; } + /// + /// Gets the user's preferences for the given preference kind. + /// + /// The preference kind. + /// A string array containing the user's preferences. public string[] GetPreference(PreferenceKind preference) { var val = Preferences @@ -279,18 +424,32 @@ namespace Jellyfin.Data.Entities return Equals(val, string.Empty) ? Array.Empty() : val.Split(Delimiter); } + /// + /// Sets the specified preference to the given value. + /// + /// The preference kind. + /// The values. public void SetPreference(PreferenceKind preference, string[] values) { Preferences.First(p => p.Kind == preference).Value = string.Join(Delimiter.ToString(CultureInfo.InvariantCulture), values); } + /// + /// Checks whether this user is currently allowed to use the server. + /// + /// True if the current time is within an access schedule, or there are no access schedules. public bool IsParentalScheduleAllowed() { return AccessSchedules.Count == 0 || AccessSchedules.Any(i => IsParentalScheduleAllowed(i, DateTime.UtcNow)); } + /// + /// Checks whether the provided folder is in this user's grouped folders. + /// + /// The Guid of the folder. + /// True if the folder is in the user's grouped folders. public bool IsFolderGrouped(Guid id) { return GetPreference(PreferenceKind.GroupedFolders).Any(i => new Guid(i) == id); diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 599efe583..e16b1fb7b 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -35,7 +35,7 @@ namespace Jellyfin.Server.Implementations.Users private readonly INetworkManager _networkManager; private readonly IApplicationHost _appHost; private readonly IImageProcessor _imageProcessor; - private readonly ILogger _logger; + private readonly ILogger _logger; private IAuthenticationProvider[] _authenticationProviders; private DefaultAuthenticationProvider _defaultAuthenticationProvider; @@ -58,7 +58,7 @@ namespace Jellyfin.Server.Implementations.Users INetworkManager networkManager, IApplicationHost appHost, IImageProcessor imageProcessor, - ILogger logger) + ILogger logger) { _dbProvider = dbProvider; _cryptoProvider = cryptoProvider; @@ -190,7 +190,7 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = _dbProvider.CreateContext(); - // Temporary measure until user item data is migrated. + // TODO: Remove after user item data is migrated. var max = dbContext.Users.Select(u => u.InternalId).Max(); var newUser = new User( -- cgit v1.2.3 From e3f9aaa9c62d44854c3ea667c670d6b5a76c0254 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 22 May 2020 21:45:31 -0400 Subject: Fix bugs relating to users not being properly locked out. --- .../Activity/ActivityLogEntryPoint.cs | 18 ++++-------------- Jellyfin.Server.Implementations/Users/UserManager.cs | 7 ++++--- 2 files changed, 8 insertions(+), 17 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 31d9609a6..05aa44338 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -104,8 +104,10 @@ namespace Emby.Server.Implementations.Activity _localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Username), NotificationType.UserLockedOut.ToString(), - e.Argument.Id)) - .ConfigureAwait(false); + e.Argument.Id) + { + LogSeverity = LogLevel.Error + }).ConfigureAwait(false); } private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e) @@ -303,18 +305,6 @@ namespace Emby.Server.Implementations.Activity }).ConfigureAwait(false); } - private async void OnUserPolicyUpdated(object sender, GenericEventArgs e) - { - await CreateLogEntry(new ActivityLog( - string.Format( - CultureInfo.InvariantCulture, - _localization.GetLocalizedString("UserPolicyUpdatedWithName"), - e.Argument.Username), - "UserPolicyUpdated", - e.Argument.Id)) - .ConfigureAwait(false); - } - private async void OnUserDeleted(object sender, GenericEventArgs e) { await CreateLogEntry(new ActivityLog( diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index e16b1fb7b..23646de61 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Runtime.InteropServices.ComTypes; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; @@ -800,16 +801,16 @@ namespace Jellyfin.Server.Implementations.Users private void IncrementInvalidLoginAttemptCount(User user) { - int invalidLogins = user.InvalidLoginAttemptCount; + user.InvalidLoginAttemptCount++; int? maxInvalidLogins = user.LoginAttemptsBeforeLockout; - if (maxInvalidLogins.HasValue && invalidLogins >= maxInvalidLogins) + if (maxInvalidLogins.HasValue && user.InvalidLoginAttemptCount >= maxInvalidLogins) { user.SetPermission(PermissionKind.IsDisabled, true); OnUserLockedOut?.Invoke(this, new GenericEventArgs(user)); _logger.LogWarning( "Disabling user {Username} due to {Attempts} unsuccessful login attempts.", user.Username, - invalidLogins); + user.InvalidLoginAttemptCount); } UpdateUser(user); -- cgit v1.2.3 From 99511b3be8dd63d832336c65b72d0c17efb9bc6b Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 23 May 2020 14:53:24 -0400 Subject: Fix a couple bugs --- .../Users/DefaultAuthenticationProvider.cs | 2 +- Jellyfin.Server.Implementations/Users/UserManager.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs index df730731a..c15312a72 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs @@ -53,7 +53,7 @@ namespace Jellyfin.Server.Implementations.Users bool success = false; // As long as jellyfin supports passwordless users, we need this little block here to accommodate - if (!HasPassword(resolvedUser)) + if (!HasPassword(resolvedUser) && string.IsNullOrEmpty(password)) { return Task.FromResult(new ProviderAuthenticationResult { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 23646de61..62c4b9b87 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Runtime.InteropServices.ComTypes; @@ -617,6 +618,12 @@ namespace Jellyfin.Server.Implementations.Users public void UpdatePolicy(Guid userId, UserPolicy policy) { var user = GetUserById(userId); + int? loginAttempts = policy.LoginAttemptsBeforeLockout switch + { + -1 => null, + 0 => 3, + _ => policy.LoginAttemptsBeforeLockout + }; user.MaxParentalAgeRating = policy.MaxParentalRating; user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess; @@ -624,9 +631,7 @@ namespace Jellyfin.Server.Implementations.Users user.AuthenticationProviderId = policy.AuthenticationProviderId; user.PasswordResetProviderId = policy.PasswordResetProviderId; user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; - user.LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1 - ? null - : new int?(policy.LoginAttemptsBeforeLockout); + user.LoginAttemptsBeforeLockout = loginAttempts; user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled); -- cgit v1.2.3 From e8173df9dc67470748c3293745fe3948363373b4 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 23 May 2020 15:33:14 -0400 Subject: Cleanup --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 -- Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 62c4b9b87..7c27a3c41 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -2,10 +2,8 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.Linq; -using System.Runtime.InteropServices.ComTypes; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index a1895247f..a19638abf 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -87,14 +87,13 @@ namespace Jellyfin.Server.Migrations.Routines policy.PasswordResetProviderId = typeof(DefaultPasswordResetProvider).FullName; - var user = new User(mockup.Name, policy.AuthenticationProviderId, string.Empty) + var user = new User(mockup.Name, policy.AuthenticationProviderId, policy.PasswordResetProviderId) { Id = entry[1].ReadGuidFromBlob(), InternalId = entry[0].ToInt64(), MaxParentalAgeRating = policy.MaxParentalRating, EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess, RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit, - PasswordResetProviderId = policy.PasswordResetProviderId, InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount, LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1 ? null : new int?(policy.LoginAttemptsBeforeLockout), SubtitleMode = config.SubtitleMode, -- cgit v1.2.3 From e052128c527672ed586a78257da1d2b07319e598 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 23 May 2020 16:07:42 -0400 Subject: Cleanup and fix more bugs --- Jellyfin.Server.Implementations/Users/UserManager.cs | 9 +++++++-- Jellyfin.Server/CoreAppHost.cs | 4 +--- Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 8 +++++++- 3 files changed, 15 insertions(+), 6 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 7c27a3c41..41116c251 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -556,6 +556,11 @@ namespace Jellyfin.Server.Implementations.Users _invalidAuthProvider = _authenticationProviders.OfType().First(); _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); + + if (_authenticationProviders.Length > 2) + { + _logger.LogCritical("INVALID NUMBER OF LOGGERS: {0}", _authenticationProviders.Length); + } } /// @@ -616,7 +621,7 @@ namespace Jellyfin.Server.Implementations.Users public void UpdatePolicy(Guid userId, UserPolicy policy) { var user = GetUserById(userId); - int? loginAttempts = policy.LoginAttemptsBeforeLockout switch + int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch { -1 => null, 0 => 3, @@ -629,7 +634,7 @@ namespace Jellyfin.Server.Implementations.Users user.AuthenticationProviderId = policy.AuthenticationProviderId; user.PasswordResetProviderId = policy.PasswordResetProviderId; user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; - user.LoginAttemptsBeforeLockout = loginAttempts; + user.LoginAttemptsBeforeLockout = maxLoginAttempts; user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled); diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs index 81ae38467..716abcb63 100644 --- a/Jellyfin.Server/CoreAppHost.cs +++ b/Jellyfin.Server/CoreAppHost.cs @@ -85,9 +85,7 @@ namespace Jellyfin.Server protected override IEnumerable GetAssembliesWithPartsInternal() { yield return typeof(CoreAppHost).Assembly; - yield return typeof(DefaultAuthenticationProvider).Assembly; - yield return typeof(DefaultPasswordResetProvider).Assembly; - yield return typeof(InvalidAuthProvider).Assembly; + yield return Assembly.Load("Jellyfin.Server.Implementations"); } /// diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index a19638abf..af74d3a1d 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -86,6 +86,12 @@ namespace Jellyfin.Server.Migrations.Routines ?? typeof(DefaultAuthenticationProvider).FullName; policy.PasswordResetProviderId = typeof(DefaultPasswordResetProvider).FullName; + int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch + { + -1 => null, + 0 => 3, + _ => policy.LoginAttemptsBeforeLockout + }; var user = new User(mockup.Name, policy.AuthenticationProviderId, policy.PasswordResetProviderId) { @@ -95,7 +101,7 @@ namespace Jellyfin.Server.Migrations.Routines EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess, RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit, InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount, - LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1 ? null : new int?(policy.LoginAttemptsBeforeLockout), + LoginAttemptsBeforeLockout = maxLoginAttempts, SubtitleMode = config.SubtitleMode, HidePlayedInLatest = config.HidePlayedInLatest, EnableLocalPassword = config.EnableLocalPassword, -- cgit v1.2.3 From 7c823464bca70570f2f53f8af6913e53d385b784 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 26 May 2020 20:52:05 -0400 Subject: Fix merge conflicts with SyncPlay --- .../SyncPlay/SyncPlayManager.cs | 64 ++++++++-------------- Jellyfin.Data/Entities/User.cs | 4 ++ Jellyfin.Data/Enums/SyncPlayAccess.cs | 23 ++++++++ .../Users/UserManager.cs | 27 +-------- MediaBrowser.Model/Configuration/SyncplayAccess.cs | 23 -------- MediaBrowser.Model/Users/UserPolicy.cs | 4 +- 6 files changed, 55 insertions(+), 90 deletions(-) create mode 100644 Jellyfin.Data/Enums/SyncPlayAccess.cs delete mode 100644 MediaBrowser.Model/Configuration/SyncplayAccess.cs (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs index 1f76dd4e3..6a3e684ca 100644 --- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs +++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs @@ -3,13 +3,14 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; -using Microsoft.Extensions.Logging; -using MediaBrowser.Controller.Entities; +using Jellyfin.Data.Entities; +using Jellyfin.Data.Enums; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Controller.SyncPlay; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.SyncPlay; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.SyncPlay { @@ -102,14 +103,6 @@ namespace Emby.Server.Implementations.SyncPlay _disposed = true; } - private void CheckDisposed() - { - if (_disposed) - { - throw new ObjectDisposedException(GetType().Name); - } - } - private void OnSessionManagerSessionEnded(object sender, SessionEventArgs e) { var session = e.SessionInfo; @@ -143,37 +136,26 @@ namespace Emby.Server.Implementations.SyncPlay // Check ParentalRating access var hasParentalRatingAccess = true; - if (user.Policy.MaxParentalRating.HasValue) + if (user.MaxParentalAgeRating.HasValue) { - hasParentalRatingAccess = item.InheritedParentalRatingValue <= user.Policy.MaxParentalRating; + hasParentalRatingAccess = item.InheritedParentalRatingValue <= user.MaxParentalAgeRating.Value; } - if (!user.Policy.EnableAllFolders && hasParentalRatingAccess) + if (!user.HasPermission(PermissionKind.EnableAllFolders) && hasParentalRatingAccess) { var collections = _libraryManager.GetCollectionFolders(item).Select( - folder => folder.Id.ToString("N", CultureInfo.InvariantCulture) - ); - var intersect = collections.Intersect(user.Policy.EnabledFolders); - return intersect.Any(); - } - else - { - return hasParentalRatingAccess; + folder => folder.Id.ToString("N", CultureInfo.InvariantCulture)); + + return collections.Intersect(user.GetPreference(PreferenceKind.EnabledFolders)).Any(); } + + return hasParentalRatingAccess; } private Guid? GetSessionGroup(SessionInfo session) { - ISyncPlayController group; - _sessionToGroupMap.TryGetValue(session.Id, out group); - if (group != null) - { - return group.GetGroupId(); - } - else - { - return null; - } + _sessionToGroupMap.TryGetValue(session.Id, out var group); + return group?.GetGroupId(); } /// @@ -181,7 +163,7 @@ namespace Emby.Server.Implementations.SyncPlay { var user = _userManager.GetUserById(session.UserId); - if (user.Policy.SyncPlayAccess != SyncPlayAccess.CreateAndJoinGroups) + if (user.SyncPlayAccess != SyncPlayAccess.CreateAndJoinGroups) { _logger.LogWarning("NewGroup: {0} does not have permission to create groups.", session.Id); @@ -189,7 +171,7 @@ namespace Emby.Server.Implementations.SyncPlay { Type = GroupUpdateType.CreateGroupDenied }; - _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), error, CancellationToken.None); + _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None); return; } @@ -212,7 +194,7 @@ namespace Emby.Server.Implementations.SyncPlay { var user = _userManager.GetUserById(session.UserId); - if (user.Policy.SyncPlayAccess == SyncPlayAccess.None) + if (user.SyncPlayAccess == SyncPlayAccess.None) { _logger.LogWarning("JoinGroup: {0} does not have access to SyncPlay.", session.Id); @@ -220,7 +202,7 @@ namespace Emby.Server.Implementations.SyncPlay { Type = GroupUpdateType.JoinGroupDenied }; - _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), error, CancellationToken.None); + _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None); return; } @@ -237,7 +219,7 @@ namespace Emby.Server.Implementations.SyncPlay { Type = GroupUpdateType.GroupDoesNotExist }; - _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), error, CancellationToken.None); + _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None); return; } @@ -250,7 +232,7 @@ namespace Emby.Server.Implementations.SyncPlay GroupId = group.GetGroupId().ToString(), Type = GroupUpdateType.LibraryAccessDenied }; - _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), error, CancellationToken.None); + _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None); return; } @@ -285,7 +267,7 @@ namespace Emby.Server.Implementations.SyncPlay { Type = GroupUpdateType.NotInGroup }; - _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), error, CancellationToken.None); + _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None); return; } @@ -304,7 +286,7 @@ namespace Emby.Server.Implementations.SyncPlay { var user = _userManager.GetUserById(session.UserId); - if (user.Policy.SyncPlayAccess == SyncPlayAccess.None) + if (user.SyncPlayAccess == SyncPlayAccess.None) { return new List(); } @@ -334,7 +316,7 @@ namespace Emby.Server.Implementations.SyncPlay { var user = _userManager.GetUserById(session.UserId); - if (user.Policy.SyncPlayAccess == SyncPlayAccess.None) + if (user.SyncPlayAccess == SyncPlayAccess.None) { _logger.LogWarning("HandleRequest: {0} does not have access to SyncPlay.", session.Id); @@ -342,7 +324,7 @@ namespace Emby.Server.Implementations.SyncPlay { Type = GroupUpdateType.JoinGroupDenied }; - _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), error, CancellationToken.None); + _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None); return; } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 2287d802b..0a4661780 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -61,6 +61,7 @@ namespace Jellyfin.Data.Entities EnableAutoLogin = false; PlayDefaultAudioTrack = true; SubtitleMode = SubtitlePlaybackMode.Default; + SyncPlayAccess = SyncPlayAccess.CreateAndJoinGroups; AddDefaultPermissions(); AddDefaultPreferences(); @@ -319,6 +320,9 @@ namespace Jellyfin.Data.Entities /// public virtual ImageInfo ProfileImage { get; set; } + [Required] + public SyncPlayAccess SyncPlayAccess { get; set; } + /// /// Gets or sets the row version. /// diff --git a/Jellyfin.Data/Enums/SyncPlayAccess.cs b/Jellyfin.Data/Enums/SyncPlayAccess.cs new file mode 100644 index 000000000..8c13b37a1 --- /dev/null +++ b/Jellyfin.Data/Enums/SyncPlayAccess.cs @@ -0,0 +1,23 @@ +namespace Jellyfin.Data.Enums +{ + /// + /// Enum SyncPlayAccess. + /// + public enum SyncPlayAccess + { + /// + /// User can create groups and join them. + /// + CreateAndJoinGroups = 0, + + /// + /// User can only join already existing groups. + /// + JoinGroups = 1, + + /// + /// SyncPlay is disabled for the user. + /// + None = 2 + } +} diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 41116c251..886c08b4c 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -352,34 +352,12 @@ namespace Jellyfin.Server.Implementations.Users EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels), EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices), EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders), - EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders) + EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders), + SyncPlayAccess = user.SyncPlayAccess } }; } - /// - public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null) - { - if (user == null) - { - throw new ArgumentNullException(nameof(user)); - } - - bool hasConfiguredPassword = GetAuthenticationProvider(user).HasPassword(user); - bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(GetAuthenticationProvider(user).GetEasyPasswordHash(user)); - - bool hasPassword = user.EnableLocalPassword && - !string.IsNullOrEmpty(remoteEndPoint) && - _networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword; - - return new PublicUserDto - { - Name = user.Username, - HasPassword = hasPassword, - HasConfiguredPassword = hasConfiguredPassword - }; - } - /// public async Task AuthenticateUser( string username, @@ -635,6 +613,7 @@ namespace Jellyfin.Server.Implementations.Users user.PasswordResetProviderId = policy.PasswordResetProviderId; user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; user.LoginAttemptsBeforeLockout = maxLoginAttempts; + user.SyncPlayAccess = policy.SyncPlayAccess; user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled); diff --git a/MediaBrowser.Model/Configuration/SyncplayAccess.cs b/MediaBrowser.Model/Configuration/SyncplayAccess.cs deleted file mode 100644 index d891a8167..000000000 --- a/MediaBrowser.Model/Configuration/SyncplayAccess.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace MediaBrowser.Model.Configuration -{ - /// - /// Enum SyncPlayAccess. - /// - public enum SyncPlayAccess - { - /// - /// User can create groups and join them. - /// - CreateAndJoinGroups, - - /// - /// User can only join already existing groups. - /// - JoinGroups, - - /// - /// SyncPlay is disabled for the user. - /// - None - } -} diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index 7ac63a0ac..66e5529e3 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -1,10 +1,10 @@ #pragma warning disable CS1591 using System; -using System.Text.Json.Serialization; using System.Xml.Serialization; -using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using MediaBrowser.Model.Configuration; +using AccessSchedule = Jellyfin.Data.Entities.AccessSchedule; namespace MediaBrowser.Model.Users { -- cgit v1.2.3 From 82b0786cc6a5c40725a94098daa4e707476fa089 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 28 May 2020 00:59:31 -0400 Subject: Remove unnecessary logging statement --- Jellyfin.Server.Implementations/Users/UserManager.cs | 5 ----- 1 file changed, 5 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 886c08b4c..5ad9e9863 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -534,11 +534,6 @@ namespace Jellyfin.Server.Implementations.Users _invalidAuthProvider = _authenticationProviders.OfType().First(); _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); - - if (_authenticationProviders.Length > 2) - { - _logger.LogCritical("INVALID NUMBER OF LOGGERS: {0}", _authenticationProviders.Length); - } } /// -- cgit v1.2.3 From d1164979123a03c5f591dc04a4809adc695d0ae0 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 28 May 2020 01:08:37 -0400 Subject: Optimize number of created DbContexts and fix default values for some fields --- Jellyfin.Server.Implementations/Users/UserManager.cs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 5ad9e9863..60d78afd0 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -321,11 +321,11 @@ namespace Jellyfin.Server.Implementations.Users { MaxParentalRating = user.MaxParentalAgeRating, EnableUserPreferenceAccess = user.EnableUserPreferenceAccess, - RemoteClientBitrateLimit = user.RemoteClientBitrateLimit.GetValueOrDefault(), + RemoteClientBitrateLimit = user.RemoteClientBitrateLimit ?? -1, AuthenticationProviderId = user.AuthenticationProviderId, PasswordResetProviderId = user.PasswordResetProviderId, InvalidLoginAttemptCount = user.InvalidLoginAttemptCount, - LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout.GetValueOrDefault(), + LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout ?? -1, IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator), IsHidden = user.HasPermission(PermissionKind.IsHidden), IsDisabled = user.HasPermission(PermissionKind.IsDisabled), @@ -490,8 +490,6 @@ namespace Jellyfin.Server.Implementations.Users { var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername); - var action = ForgotPasswordAction.InNetworkRequired; - if (user != null && isInNetwork) { var passwordResetProvider = GetPasswordResetProvider(user); @@ -500,7 +498,7 @@ namespace Jellyfin.Server.Implementations.Users return new ForgotPasswordResult { - Action = action, + Action = ForgotPasswordAction.InNetworkRequired, PinFile = string.Empty }; } @@ -569,7 +567,8 @@ namespace Jellyfin.Server.Implementations.Users /// public void UpdateConfiguration(Guid userId, UserConfiguration config) { - var user = GetUserById(userId); + var dbContext = _dbProvider.CreateContext(); + var user = dbContext.Users.Find(userId) ?? throw new ArgumentException("No user exists with given Id!"); user.SubtitleMode = config.SubtitleMode; user.HidePlayedInLatest = config.HidePlayedInLatest; user.EnableLocalPassword = config.EnableLocalPassword; @@ -587,13 +586,15 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes); user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); - UpdateUser(user); + dbContext.Update(user); + dbContext.SaveChanges(); } /// public void UpdatePolicy(Guid userId, UserPolicy policy) { - var user = GetUserById(userId); + var dbContext = _dbProvider.CreateContext(); + var user = dbContext.Users.Find(userId) ?? throw new ArgumentException("No user exists with given Id!"); int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch { -1 => null, @@ -642,6 +643,9 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); + + dbContext.Update(user); + dbContext.SaveChanges(); } private bool IsValidUsername(string name) -- cgit v1.2.3 From 8ca78f33e9769f823fe079e90b62b561646709d7 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 28 May 2020 14:21:26 -0400 Subject: Fix bug when migrating user db with users that have never logged in. --- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- .../SyncPlay/SyncPlayManager.cs | 18 ++++++------------ Jellyfin.Data/Entities/User.cs | 4 ++-- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 4 ++-- 5 files changed, 12 insertions(+), 18 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 70b3bda6e..94423c287 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -282,7 +282,7 @@ namespace Emby.Server.Implementations.Session if (user != null) { - var userLastActivityDate = user.LastActivityDate; + var userLastActivityDate = user.LastActivityDate ?? DateTime.MinValue; user.LastActivityDate = activityDate; if ((activityDate - userLastActivityDate).TotalSeconds > 60) diff --git a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs index f44b32c36..8885266d3 100644 --- a/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs +++ b/Emby.Server.Implementations/SyncPlay/SyncPlayManager.cs @@ -134,11 +134,8 @@ namespace Emby.Server.Implementations.SyncPlay var item = _libraryManager.GetItemById(itemId); // Check ParentalRating access - var hasParentalRatingAccess = true; - if (user.MaxParentalAgeRating.HasValue) - { - hasParentalRatingAccess = item.InheritedParentalRatingValue <= user.MaxParentalAgeRating.Value; - } + var hasParentalRatingAccess = !user.MaxParentalAgeRating.HasValue + || item.InheritedParentalRatingValue <= user.MaxParentalAgeRating; if (!user.HasPermission(PermissionKind.EnableAllFolders) && hasParentalRatingAccess) { @@ -255,8 +252,7 @@ namespace Emby.Server.Implementations.SyncPlay // TODO: determine what happens to users that are in a group and get their permissions revoked lock (_groupsLock) { - ISyncPlayController group; - _sessionToGroupMap.TryGetValue(session.Id, out group); + _sessionToGroupMap.TryGetValue(session.Id, out var group); if (group == null) { @@ -329,8 +325,7 @@ namespace Emby.Server.Implementations.SyncPlay lock (_groupsLock) { - ISyncPlayController group; - _sessionToGroupMap.TryGetValue(session.Id, out group); + _sessionToGroupMap.TryGetValue(session.Id, out var group); if (group == null) { @@ -340,7 +335,7 @@ namespace Emby.Server.Implementations.SyncPlay { Type = GroupUpdateType.NotInGroup }; - _sessionManager.SendSyncPlayGroupUpdate(session.Id.ToString(), error, CancellationToken.None); + _sessionManager.SendSyncPlayGroupUpdate(session.Id, error, CancellationToken.None); return; } @@ -367,8 +362,7 @@ namespace Emby.Server.Implementations.SyncPlay throw new InvalidOperationException("Session not in any group!"); } - ISyncPlayController tempGroup; - _sessionToGroupMap.Remove(session.Id, out tempGroup); + _sessionToGroupMap.Remove(session.Id, out var tempGroup); if (!tempGroup.GetGroupId().Equals(group.GetGroupId())) { diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index cef2edfa9..1098cdb2f 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -181,12 +181,12 @@ namespace Jellyfin.Data.Entities /// /// Gets or sets the last activity date. /// - public DateTime LastActivityDate { get; set; } + public DateTime? LastActivityDate { get; set; } /// /// Gets or sets the last login date. /// - public DateTime LastLoginDate { get; set; } + public DateTime? LastLoginDate { get; set; } /// /// Gets or sets the number of login attempts the user can make before they are locked out. diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 60d78afd0..3d473f5f2 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -218,7 +218,7 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = _dbProvider.CreateContext(); - if (!dbContext.Users.Contains(user)) + if (dbContext.Users.Find(user.Id) == null) { throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 53c93f64b..2be10c708 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -196,9 +196,9 @@ namespace Jellyfin.Server.Migrations.Routines public string EasyPassword { get; set; } - public DateTime LastLoginDate { get; set; } + public DateTime? LastLoginDate { get; set; } - public DateTime LastActivityDate { get; set; } + public DateTime? LastActivityDate { get; set; } public string Name { get; set; } -- cgit v1.2.3 From 4857b7d62097848eda7f3666b60e039b1df5b6b1 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 29 May 2020 15:32:31 -0400 Subject: Make UserManager.IsValidUsername static --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 3d473f5f2..b62c64830 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -648,7 +648,7 @@ namespace Jellyfin.Server.Implementations.Users dbContext.SaveChanges(); } - private bool IsValidUsername(string name) + private static bool IsValidUsername(string name) { // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness -- cgit v1.2.3 From 1b297eae7834e8604698ae780c4b2ced7d20897d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 30 May 2020 00:20:59 -0400 Subject: Reset invalid login attempt count properly --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index b62c64830..91d0e5b80 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -467,10 +467,10 @@ namespace Jellyfin.Server.Implementations.Users if (isUserSession) { user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow; - await UpdateUserAsync(user).ConfigureAwait(false); } user.InvalidLoginAttemptCount = 0; + await UpdateUserAsync(user).ConfigureAwait(false); _logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Username); } else -- cgit v1.2.3 From 98142613e8cd8266130847210fb6bfadc1ae11bd Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 7 Jun 2020 13:36:43 -0400 Subject: Apply review suggestions and fix bug --- Jellyfin.Data/Entities/User.cs | 5 +---- .../Users/UserManager.cs | 26 ++++------------------ MediaBrowser.Controller/Entities/Folder.cs | 7 +++--- 3 files changed, 9 insertions(+), 29 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index f09b36bde..cd6cad992 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -420,10 +420,7 @@ namespace Jellyfin.Data.Entities /// A string array containing the user's preferences. public string[] GetPreference(PreferenceKind preference) { - var val = Preferences - .Where(p => p.Kind == preference) - .Select(p => p.Value) - .First(); + var val = Preferences.First(p => p.Kind == preference).Value; return Equals(val, string.Empty) ? Array.Empty() : val.Split(Delimiter); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 91d0e5b80..01151e65e 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -84,24 +84,10 @@ namespace Jellyfin.Server.Implementations.Users public event EventHandler> OnUserLockedOut; /// - public IEnumerable Users - { - get - { - var dbContext = _dbProvider.CreateContext(); - return dbContext.Users; - } - } + public IEnumerable Users => _dbProvider.CreateContext().Users; /// - public IEnumerable UsersIds - { - get - { - var dbContext = _dbProvider.CreateContext(); - return dbContext.Users.Select(u => u.Id); - } - } + public IEnumerable UsersIds => _dbProvider.CreateContext().Users.Select(u => u.Id); /// public User GetUserById(Guid id) @@ -111,9 +97,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Guid can't be empty", nameof(id)); } - var dbContext = _dbProvider.CreateContext(); - - return dbContext.Users.Find(id); + return _dbProvider.CreateContext().Users.Find(id); } /// @@ -124,11 +108,9 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(name)); } - var dbContext = _dbProvider.CreateContext(); - // This can't use an overload with StringComparer because that would cause the query to // have to be evaluated client-side. - return dbContext.Users.FirstOrDefault(u => string.Equals(u.Username, name)); + return _dbProvider.CreateContext().Users.FirstOrDefault(u => string.Equals(u.Username, name)); } /// diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 2f9bc38df..4af74f9cd 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -182,12 +182,13 @@ namespace MediaBrowser.Controller.Entities { if (this is ICollectionFolder && !(this is BasePluginFolder)) { - if (user.GetPreference(PreferenceKind.BlockedMediaFolders) != null) + var blockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders); + if (blockedMediaFolders.Length > 0) { - if (user.GetPreference(PreferenceKind.BlockedMediaFolders).Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) || + if (blockedMediaFolders.Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase) || // Backwards compatibility - user.GetPreference(PreferenceKind.BlockedMediaFolders).Contains(Name, StringComparer.OrdinalIgnoreCase)) + blockedMediaFolders.Contains(Name, StringComparer.OrdinalIgnoreCase)) { return false; } -- cgit v1.2.3 From 3d87c4c1b6bca920f444b4a16a99553d0ff6879e Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 7 Jun 2020 14:55:37 -0600 Subject: Fix EasyPassword setting --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 01151e65e..35ec78f5c 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -262,7 +262,7 @@ namespace Jellyfin.Server.Implementations.Users /// public void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1) { - GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1); + user.EasyPassword = _cryptoProvider.CreatePasswordHash(newPassword).ToString(); UpdateUser(user); OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); -- cgit v1.2.3 From 75af0a4e57ac29fb8143717055c8df1fedeba6ec Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 7 Jun 2020 19:37:47 -0400 Subject: Implement more review suggestions --- Jellyfin.Server.Implementations/Users/UserManager.cs | 4 ++-- MediaBrowser.Api/UserService.cs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 01151e65e..d3cd29909 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -276,7 +276,7 @@ namespace Jellyfin.Server.Implementations.Users Name = user.Username, Id = user.Id, ServerId = _appHost.SystemId, - HasPassword = user.Password != null, + HasPassword = GetAuthenticationProvider(user).HasPassword(user), EnableAutoLogin = user.EnableAutoLogin, LastLoginDate = user.LastLoginDate, LastActivityDate = user.LastActivityDate, @@ -303,7 +303,7 @@ namespace Jellyfin.Server.Implementations.Users { MaxParentalRating = user.MaxParentalAgeRating, EnableUserPreferenceAccess = user.EnableUserPreferenceAccess, - RemoteClientBitrateLimit = user.RemoteClientBitrateLimit ?? -1, + RemoteClientBitrateLimit = user.RemoteClientBitrateLimit ?? 0, AuthenticationProviderId = user.AuthenticationProviderId, PasswordResetProviderId = user.PasswordResetProviderId, InvalidLoginAttemptCount = user.InvalidLoginAttemptCount, diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 7bf4f88f4..9cb9baf63 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -566,7 +566,6 @@ namespace MediaBrowser.Api AssertCanUpdateUser(_authContext, _userManager, request.Id, false); _userManager.UpdateConfiguration(request.Id, request); - } public void Post(UpdateUserPolicy request) -- cgit v1.2.3 From 824cd87b75154176d6fd35f74f907f8f6ef264be Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 7 Jun 2020 20:16:51 -0400 Subject: Add missing property --- Jellyfin.Server.Implementations/Users/UserManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d3cd29909..5ed3758fb 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -277,6 +277,7 @@ namespace Jellyfin.Server.Implementations.Users Id = user.Id, ServerId = _appHost.SystemId, HasPassword = GetAuthenticationProvider(user).HasPassword(user), + HasConfiguredEasyPassword = !string.IsNullOrEmpty(user.EasyPassword), EnableAutoLogin = user.EnableAutoLogin, LastLoginDate = user.LastLoginDate, LastActivityDate = user.LastActivityDate, -- cgit v1.2.3 From d85308b4747b62ec510760229c25f8b863ff7abc Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 7 Jun 2020 23:11:51 -0400 Subject: Add another missing property --- Jellyfin.Server.Implementations/Users/UserManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 5ed3758fb..2d077a6b2 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -271,12 +271,14 @@ namespace Jellyfin.Server.Implementations.Users /// public UserDto GetUserDto(User user, string remoteEndPoint = null) { + var hasPassword = GetAuthenticationProvider(user).HasPassword(user); return new UserDto { Name = user.Username, Id = user.Id, ServerId = _appHost.SystemId, - HasPassword = GetAuthenticationProvider(user).HasPassword(user), + HasPassword = hasPassword, + HasConfiguredPassword = hasPassword, HasConfiguredEasyPassword = !string.IsNullOrEmpty(user.EasyPassword), EnableAutoLogin = user.EnableAutoLogin, LastLoginDate = user.LastLoginDate, -- cgit v1.2.3 From ce737c31ec3673caed8673253bd7c5efe7bde4a8 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 9 Jun 2020 12:21:21 -0400 Subject: Enable nullable annotations --- .../Users/DefaultAuthenticationProvider.cs | 4 +- .../Users/DefaultPasswordResetProvider.cs | 3 ++ .../Users/DeviceAccessEntryPoint.cs | 5 ++- .../Users/InvalidAuthProvider.cs | 2 + .../Users/UserManager.cs | 45 +++++++++++----------- 5 files changed, 34 insertions(+), 25 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs index b0c02030e..162dc6f5e 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Linq; using System.Text; @@ -129,7 +131,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public string GetEasyPasswordHash(User user) + public string? GetEasyPasswordHash(User user) { return string.IsNullOrEmpty(user.EasyPassword) ? null diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs index 36c95586a..cf5a01f08 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs @@ -1,3 +1,5 @@ +#nullable enable + using System; using System.Collections.Generic; using System.IO; @@ -128,6 +130,7 @@ namespace Jellyfin.Server.Implementations.Users }; } +#nullable disable private class SerializablePasswordReset : PasswordPinCreationResult { public string Pin { get; set; } diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs index d94a27b9d..140853e52 100644 --- a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs +++ b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs @@ -1,4 +1,5 @@ -#pragma warning disable CS1591 +#nullable enable +#pragma warning disable CS1591 using System.Threading.Tasks; using Jellyfin.Data.Entities; @@ -38,7 +39,7 @@ namespace Jellyfin.Server.Implementations.Users { } - private void OnUserUpdated(object sender, GenericEventArgs e) + private void OnUserUpdated(object? sender, GenericEventArgs e) { var user = e.Argument; if (!user.HasPermission(PermissionKind.EnableAllDevices)) diff --git a/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs index b6e65b559..491aba1d4 100644 --- a/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs +++ b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs @@ -1,3 +1,5 @@ +#nullable enable + using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Authentication; diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 2d077a6b2..e1084627b 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -1,4 +1,5 @@ -#pragma warning disable CA1307 +#nullable enable +#pragma warning disable CA1307 using System; using System.Collections.Generic; @@ -37,11 +38,11 @@ namespace Jellyfin.Server.Implementations.Users private readonly IImageProcessor _imageProcessor; private readonly ILogger _logger; - private IAuthenticationProvider[] _authenticationProviders; - private DefaultAuthenticationProvider _defaultAuthenticationProvider; - private InvalidAuthProvider _invalidAuthProvider; - private IPasswordResetProvider[] _passwordResetProviders; - private DefaultPasswordResetProvider _defaultPasswordResetProvider; + private IAuthenticationProvider[] _authenticationProviders = null!; + private DefaultAuthenticationProvider _defaultAuthenticationProvider = null!; + private InvalidAuthProvider _invalidAuthProvider = null!; + private IPasswordResetProvider[] _passwordResetProviders = null!; + private DefaultPasswordResetProvider _defaultPasswordResetProvider = null!; /// /// Initializes a new instance of the class. @@ -69,19 +70,19 @@ namespace Jellyfin.Server.Implementations.Users } /// - public event EventHandler> OnUserPasswordChanged; + public event EventHandler>? OnUserPasswordChanged; /// - public event EventHandler> OnUserUpdated; + public event EventHandler>? OnUserUpdated; /// - public event EventHandler> OnUserCreated; + public event EventHandler>? OnUserCreated; /// - public event EventHandler> OnUserDeleted; + public event EventHandler>? OnUserDeleted; /// - public event EventHandler> OnUserLockedOut; + public event EventHandler>? OnUserLockedOut; /// public IEnumerable Users => _dbProvider.CreateContext().Users; @@ -90,7 +91,7 @@ namespace Jellyfin.Server.Implementations.Users public IEnumerable UsersIds => _dbProvider.CreateContext().Users.Select(u => u.Id); /// - public User GetUserById(Guid id) + public User? GetUserById(Guid id) { if (id == Guid.Empty) { @@ -101,7 +102,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public User GetUserByName(string name) + public User? GetUserByName(string name) { if (string.IsNullOrWhiteSpace(name)) { @@ -260,7 +261,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1) + public void ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1) { GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1); UpdateUser(user); @@ -269,7 +270,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public UserDto GetUserDto(User user, string remoteEndPoint = null) + public UserDto GetUserDto(User user, string? remoteEndPoint = null) { var hasPassword = GetAuthenticationProvider(user).HasPassword(user); return new UserDto @@ -344,7 +345,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public async Task AuthenticateUser( + public async Task AuthenticateUser( string username, string password, string passwordSha1, @@ -359,7 +360,7 @@ namespace Jellyfin.Server.Implementations.Users var user = Users.ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); bool success; - IAuthenticationProvider authenticationProvider; + IAuthenticationProvider? authenticationProvider; if (user != null) { @@ -651,7 +652,7 @@ namespace Jellyfin.Server.Implementations.Users return GetPasswordResetProviders(user)[0]; } - private IList GetAuthenticationProviders(User user) + private IList GetAuthenticationProviders(User? user) { var authenticationProviderId = user?.AuthenticationProviderId; @@ -701,14 +702,14 @@ namespace Jellyfin.Server.Implementations.Users return providers; } - private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser( + private async Task<(IAuthenticationProvider? authenticationProvider, string username, bool success)> AuthenticateLocalUser( string username, string password, - User user, + User? user, string remoteEndPoint) { bool success = false; - IAuthenticationProvider authenticationProvider = null; + IAuthenticationProvider? authenticationProvider = null; foreach (var provider in GetAuthenticationProviders(user)) { @@ -746,7 +747,7 @@ namespace Jellyfin.Server.Implementations.Users IAuthenticationProvider provider, string username, string password, - User resolvedUser) + User? resolvedUser) { try { -- cgit v1.2.3 From d105bc728d0ca44b601b311eca493c1171fa71fa Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 9 Jun 2020 14:01:21 -0400 Subject: Fix startup wizard. --- Jellyfin.Api/Controllers/StartupController.cs | 4 ++- .../Users/UserManager.cs | 33 ++++++++++++++++++++++ MediaBrowser.Controller/Library/IUserManager.cs | 5 ++++ 3 files changed, 41 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index f965d83f3..6ec0a4e26 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -95,6 +95,8 @@ namespace Jellyfin.Api.Controllers [HttpGet("User")] public StartupUserDto GetFirstUser() { + // TODO: Remove this method when startup wizard no longer requires an existing user. + _userManager.Initialize(); var user = _userManager.Users.First(); return new StartupUserDto { @@ -115,7 +117,7 @@ namespace Jellyfin.Api.Controllers user.Username = startupUserDto.Name; - _userManager.UpdateUser(user); + await _userManager.UpdateUserAsync(user).ConfigureAwait(false); if (!string.IsNullOrEmpty(startupUserDto.Password)) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index e1084627b..0ea13f4e7 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -520,6 +520,39 @@ namespace Jellyfin.Server.Implementations.Users _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); } + /// + public void Initialize() + { + // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. + var dbContext = _dbProvider.CreateContext(); + + if (dbContext.Users.Any()) + { + return; + } + + var defaultName = Environment.UserName; + if (string.IsNullOrWhiteSpace(defaultName)) + { + defaultName = "MyJellyfinUser"; + } + + _logger.LogWarning("No users, creating one with username {UserName}", defaultName); + + if (!IsValidUsername(defaultName)) + { + throw new ArgumentException("Provided username is not valid!", defaultName); + } + + var newUser = CreateUser(defaultName); + newUser.SetPermission(PermissionKind.IsAdministrator, true); + newUser.SetPermission(PermissionKind.EnableContentDeletion, true); + newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true); + + dbContext.Users.Add(newUser); + dbContext.SaveChanges(); + } + /// public NameIdPair[] GetAuthenticationProviders() { diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 1e385dcb9..74f117f15 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -52,6 +52,11 @@ namespace MediaBrowser.Controller.Library /// The users ids. IEnumerable UsersIds { get; } + /// + /// Initializes the user manager and ensures that a user exists. + /// + void Initialize(); + /// /// Gets a user by Id. /// -- cgit v1.2.3 From 4d559b4ec45c66554230d402c5dcc3f616576b0e Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 10 Jun 2020 20:14:24 -0400 Subject: Fix bugs for fresh installs. --- Jellyfin.Server.Implementations/Users/UserManager.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 0ea13f4e7..d05fc2549 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -22,6 +22,7 @@ using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Events; using MediaBrowser.Model.Users; +using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Implementations.Users @@ -174,7 +175,7 @@ namespace Jellyfin.Server.Implementations.Users var dbContext = _dbProvider.CreateContext(); // TODO: Remove after user item data is migrated. - var max = dbContext.Users.Select(u => u.InternalId).Max(); + var max = dbContext.Users.Any() ? dbContext.Users.Select(u => u.InternalId).Max() : 0; var newUser = new User( name, @@ -549,7 +550,7 @@ namespace Jellyfin.Server.Implementations.Users newUser.SetPermission(PermissionKind.EnableContentDeletion, true); newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true); - dbContext.Users.Add(newUser); + dbContext.Users.Update(newUser); dbContext.SaveChanges(); } -- cgit v1.2.3 From 7fba0b778ed7d1d8f8c5ff4e60945872319722c5 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 11 Jun 2020 17:51:02 -0400 Subject: Properly remove profile images --- Jellyfin.Server.Implementations/Users/UserManager.cs | 11 ++++++++++- MediaBrowser.Api/Images/ImageService.cs | 2 +- MediaBrowser.Controller/Library/IUserManager.cs | 10 ++++++++-- 3 files changed, 19 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d05fc2549..68f038ae8 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -22,7 +22,6 @@ using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Events; using MediaBrowser.Model.Users; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Implementations.Users @@ -668,6 +667,16 @@ namespace Jellyfin.Server.Implementations.Users dbContext.SaveChanges(); } + public void ClearProfileImage(User user) + { +#nullable disable + // TODO: Remove these when User has nullable annotations + + // Can't just set the value to null, as it hasn't been loaded yet, so EF Core wouldn't see the change + _dbProvider.CreateContext().Entry(user).Reference(u => u.ProfileImage).CurrentValue = null; +#nullable enable + } + private static bool IsValidUsername(string name) { // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ diff --git a/MediaBrowser.Api/Images/ImageService.cs b/MediaBrowser.Api/Images/ImageService.cs index d284dd55c..fa73dca63 100644 --- a/MediaBrowser.Api/Images/ImageService.cs +++ b/MediaBrowser.Api/Images/ImageService.cs @@ -489,7 +489,7 @@ namespace MediaBrowser.Api.Images Logger.LogError(e, "Error deleting user profile image:"); } - user.ProfileImage = null; + _userManager.ClearProfileImage(user); _userManager.UpdateUser(user); } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 74f117f15..b5b2e4729 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -175,7 +175,7 @@ namespace MediaBrowser.Controller.Library /// /// This method updates the user's configuration. /// This is only included as a stopgap until the new API, using this internally is not recommended. - /// Instead, modify the user object directlu, then call . + /// Instead, modify the user object directly, then call . /// /// The user's Id. /// The request containing the new user configuration. @@ -184,10 +184,16 @@ namespace MediaBrowser.Controller.Library /// /// This method updates the user's policy. /// This is only included as a stopgap until the new API, using this internally is not recommended. - /// Instead, modify the user object directlu, then call . + /// Instead, modify the user object directly, then call . /// /// The user's Id. /// The request containing the new user policy. void UpdatePolicy(Guid userId, UserPolicy policy); + + /// + /// Clears the user's profile image. + /// + /// The user. + void ClearProfileImage(User user); } } -- cgit v1.2.3 From fc02157b42fe5e4f04681cb82ace9865aaa550cc Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 11 Jun 2020 18:28:49 -0400 Subject: Fix build errors --- Emby.Server.Implementations/Library/UserDataManager.cs | 1 + Jellyfin.Server.Implementations/Users/UserManager.cs | 1 + MediaBrowser.Api/Library/LibraryService.cs | 1 + MediaBrowser.Api/Movies/MoviesService.cs | 1 + MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs | 1 + MediaBrowser.Controller/Entities/Audio/MusicArtist.cs | 1 + MediaBrowser.Controller/Entities/TV/Series.cs | 1 + MediaBrowser.Controller/Entities/UserViewBuilder.cs | 1 + 8 files changed, 8 insertions(+) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Library/UserDataManager.cs b/Emby.Server.Implementations/Library/UserDataManager.cs index e56ef2328..803fe34fb 100644 --- a/Emby.Server.Implementations/Library/UserDataManager.cs +++ b/Emby.Server.Implementations/Library/UserDataManager.cs @@ -14,6 +14,7 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; +using Book = MediaBrowser.Controller.Entities.Book; namespace Emby.Server.Implementations.Library { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 68f038ae8..169f89467 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -667,6 +667,7 @@ namespace Jellyfin.Server.Implementations.Users dbContext.SaveChanges(); } + /// public void ClearProfileImage(User user) { #nullable disable diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 99a457cee..2b4694925 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -29,6 +29,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Net.Http.Headers; using Book = MediaBrowser.Controller.Entities.Book; using Episode = MediaBrowser.Controller.Entities.TV.Episode; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; using Movie = MediaBrowser.Controller.Entities.Movies.Movie; using MusicAlbum = MediaBrowser.Controller.Entities.Audio.MusicAlbum; using Series = MediaBrowser.Controller.Entities.TV.Series; diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index c5c996a67..2d61299c7 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -15,6 +15,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Services; using Microsoft.Extensions.Logging; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; using Movie = MediaBrowser.Controller.Entities.Movies.Movie; namespace MediaBrowser.Api.Movies diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 6c33831ba..f7b2f9549 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -10,6 +10,7 @@ using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; namespace MediaBrowser.Controller.Entities.Audio { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index db1edfe27..63db3cfab 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -10,6 +10,7 @@ using MediaBrowser.Controller.Extensions; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using Microsoft.Extensions.Logging; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; namespace MediaBrowser.Controller.Entities.Audio { diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index 10a8b3370..a519089b3 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -12,6 +12,7 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; using MediaBrowser.Model.Querying; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; namespace MediaBrowser.Controller.Entities.TV { diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index d5896eae4..52fbdd825 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -11,6 +11,7 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using Microsoft.Extensions.Logging; using Episode = MediaBrowser.Controller.Entities.TV.Episode; +using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider; using Movie = MediaBrowser.Controller.Entities.Movies.Movie; using Season = MediaBrowser.Controller.Entities.TV.Season; using Series = MediaBrowser.Controller.Entities.TV.Series; -- cgit v1.2.3 From 103c9b716286204011606a22156c01250d69a0fd Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 12 Jun 2020 22:47:09 -0400 Subject: Actually fix deleting profile images (hopefully) --- Jellyfin.Server.Implementations/JellyfinDb.cs | 4 ++++ Jellyfin.Server.Implementations/Users/UserManager.cs | 9 +++------ 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index d3c0267e8..f574ebc66 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -23,8 +23,12 @@ namespace Jellyfin.Server.Implementations /// public static string ConnectionString { get; set; } = @"Data Source=jellyfin.db"; + public virtual DbSet AccessSchedules { get; set; } + public virtual DbSet ActivityLogs { get; set; } + public virtual DbSet ImageInfos { get; set; } + public virtual DbSet Permissions { get; set; } public virtual DbSet Preferences { get; set; } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 169f89467..bab6cf3fc 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -670,12 +670,9 @@ namespace Jellyfin.Server.Implementations.Users /// public void ClearProfileImage(User user) { -#nullable disable - // TODO: Remove these when User has nullable annotations - - // Can't just set the value to null, as it hasn't been loaded yet, so EF Core wouldn't see the change - _dbProvider.CreateContext().Entry(user).Reference(u => u.ProfileImage).CurrentValue = null; -#nullable enable + var dbContext = _dbProvider.CreateContext(); + dbContext.ImageInfos.Remove(user.ProfileImage); + dbContext.SaveChanges(); } private static bool IsValidUsername(string name) -- cgit v1.2.3 From 32780154440d5e7d26d3cb4b32a43d1d8d2735f3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 13 Jun 2020 16:38:17 -0400 Subject: Actually fix profile images --- Jellyfin.Data/Entities/AccessSchedule.cs | 1 - Jellyfin.Data/Entities/ImageInfo.cs | 2 + Jellyfin.Data/Entities/User.cs | 1 + .../Migrations/20200613155524_AddUsers.Designer.cs | 312 --------------------- .../Migrations/20200613155524_AddUsers.cs | 196 ------------- .../Migrations/20200613202153_AddUsers.Designer.cs | 312 +++++++++++++++++++++ .../Migrations/20200613202153_AddUsers.cs | 197 +++++++++++++ .../Migrations/JellyfinDbModelSnapshot.cs | 25 +- .../Users/UserManager.cs | 2 +- 9 files changed, 526 insertions(+), 522 deletions(-) delete mode 100644 Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.Designer.cs delete mode 100644 Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs create mode 100644 Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs index 15c4e4cde..7d1b76a3f 100644 --- a/Jellyfin.Data/Entities/AccessSchedule.cs +++ b/Jellyfin.Data/Entities/AccessSchedule.cs @@ -52,7 +52,6 @@ namespace Jellyfin.Data.Entities /// [XmlIgnore] [Required] - [ForeignKey("Id")] public Guid UserId { get; protected set; } /// diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index 8bbce95e4..64e36a791 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -17,6 +17,8 @@ namespace Jellyfin.Data.Entities [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } + public Guid? UserId { get; protected set; } + [Required] [MaxLength(512)] [StringLength(512)] diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 7d99be542..b89b0a8f4 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -324,6 +324,7 @@ namespace Jellyfin.Data.Entities /// /// Gets or sets the user's profile image. Can be null. /// + // [ForeignKey("UserId")] public virtual ImageInfo ProfileImage { get; set; } [Required] diff --git a/Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.Designer.cs deleted file mode 100644 index c43b035d9..000000000 --- a/Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.Designer.cs +++ /dev/null @@ -1,312 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1601 - -// -using System; -using Jellyfin.Server.Implementations; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -namespace Jellyfin.Server.Implementations.Migrations -{ - [DbContext(typeof(JellyfinDb))] - [Migration("20200613155524_AddUsers")] - partial class AddUsers - { - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasDefaultSchema("jellyfin") - .HasAnnotation("ProductVersion", "3.1.4"); - - modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DayOfWeek") - .HasColumnType("INTEGER"); - - b.Property("EndHour") - .HasColumnType("REAL"); - - b.Property("StartHour") - .HasColumnType("REAL"); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.HasIndex("UserId"); - - b.ToTable("AccessSchedules"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("DateCreated") - .HasColumnType("TEXT"); - - b.Property("ItemId") - .HasColumnType("TEXT") - .HasMaxLength(256); - - b.Property("LogSeverity") - .HasColumnType("INTEGER"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(512); - - b.Property("Overview") - .HasColumnType("TEXT") - .HasMaxLength(512); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("ShortOverview") - .HasColumnType("TEXT") - .HasMaxLength(512); - - b.Property("Type") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(256); - - b.Property("UserId") - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("ActivityLogs"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("LastModified") - .HasColumnType("TEXT"); - - b.Property("Path") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(512); - - b.HasKey("Id"); - - b.ToTable("ImageInfos"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Permission_Permissions_Guid") - .HasColumnType("TEXT"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .HasColumnType("INTEGER"); - - b.HasKey("Id"); - - b.HasIndex("Permission_Permissions_Guid"); - - b.ToTable("Permissions"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Kind") - .HasColumnType("INTEGER"); - - b.Property("Preference_Preferences_Guid") - .HasColumnType("TEXT"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("Value") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.HasKey("Id"); - - b.HasIndex("Preference_Preferences_Guid"); - - b.ToTable("Preferences"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.User", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("TEXT"); - - b.Property("AudioLanguagePreference") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("AuthenticationProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("DisplayCollectionsView") - .HasColumnType("INTEGER"); - - b.Property("DisplayMissingEpisodes") - .HasColumnType("INTEGER"); - - b.Property("EasyPassword") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("EnableAutoLogin") - .HasColumnType("INTEGER"); - - b.Property("EnableLocalPassword") - .HasColumnType("INTEGER"); - - b.Property("EnableNextEpisodeAutoPlay") - .HasColumnType("INTEGER"); - - b.Property("EnableUserPreferenceAccess") - .HasColumnType("INTEGER"); - - b.Property("HidePlayedInLatest") - .HasColumnType("INTEGER"); - - b.Property("InternalId") - .HasColumnType("INTEGER"); - - b.Property("InvalidLoginAttemptCount") - .HasColumnType("INTEGER"); - - b.Property("LastActivityDate") - .HasColumnType("TEXT"); - - b.Property("LastLoginDate") - .HasColumnType("TEXT"); - - b.Property("LoginAttemptsBeforeLockout") - .HasColumnType("INTEGER"); - - b.Property("MaxParentalAgeRating") - .HasColumnType("INTEGER"); - - b.Property("MustUpdatePassword") - .HasColumnType("INTEGER"); - - b.Property("Password") - .HasColumnType("TEXT") - .HasMaxLength(65535); - - b.Property("PasswordResetProviderId") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("PlayDefaultAudioTrack") - .HasColumnType("INTEGER"); - - b.Property("ProfileImageId") - .HasColumnType("INTEGER"); - - b.Property("RememberAudioSelections") - .HasColumnType("INTEGER"); - - b.Property("RememberSubtitleSelections") - .HasColumnType("INTEGER"); - - b.Property("RemoteClientBitrateLimit") - .HasColumnType("INTEGER"); - - b.Property("RowVersion") - .IsConcurrencyToken() - .HasColumnType("INTEGER"); - - b.Property("SubtitleLanguagePreference") - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.Property("SubtitleMode") - .HasColumnType("INTEGER"); - - b.Property("SyncPlayAccess") - .HasColumnType("INTEGER"); - - b.Property("Username") - .IsRequired() - .HasColumnType("TEXT") - .HasMaxLength(255); - - b.HasKey("Id"); - - b.HasIndex("ProfileImageId"); - - b.ToTable("Users"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => - { - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("AccessSchedules") - .HasForeignKey("UserId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired(); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => - { - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Permissions") - .HasForeignKey("Permission_Permissions_Guid"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => - { - b.HasOne("Jellyfin.Data.Entities.User", null) - .WithMany("Preferences") - .HasForeignKey("Preference_Preferences_Guid"); - }); - - modelBuilder.Entity("Jellyfin.Data.Entities.User", b => - { - b.HasOne("Jellyfin.Data.Entities.ImageInfo", "ProfileImage") - .WithMany() - .HasForeignKey("ProfileImageId"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.cs b/Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.cs deleted file mode 100644 index 369458b6c..000000000 --- a/Jellyfin.Server.Implementations/Migrations/20200613155524_AddUsers.cs +++ /dev/null @@ -1,196 +0,0 @@ -#pragma warning disable CS1591 -#pragma warning disable SA1601 - -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -namespace Jellyfin.Server.Implementations.Migrations -{ - public partial class AddUsers : Migration - { - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "ImageInfos", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Path = table.Column(maxLength: 512, nullable: false), - LastModified = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_ImageInfos", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Users", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false), - Username = table.Column(maxLength: 255, nullable: false), - Password = table.Column(maxLength: 65535, nullable: true), - EasyPassword = table.Column(maxLength: 65535, nullable: true), - MustUpdatePassword = table.Column(nullable: false), - AudioLanguagePreference = table.Column(maxLength: 255, nullable: true), - AuthenticationProviderId = table.Column(maxLength: 255, nullable: false), - PasswordResetProviderId = table.Column(maxLength: 255, nullable: false), - InvalidLoginAttemptCount = table.Column(nullable: false), - LastActivityDate = table.Column(nullable: true), - LastLoginDate = table.Column(nullable: true), - LoginAttemptsBeforeLockout = table.Column(nullable: true), - SubtitleMode = table.Column(nullable: false), - PlayDefaultAudioTrack = table.Column(nullable: false), - SubtitleLanguagePreference = table.Column(maxLength: 255, nullable: true), - DisplayMissingEpisodes = table.Column(nullable: false), - DisplayCollectionsView = table.Column(nullable: false), - EnableLocalPassword = table.Column(nullable: false), - HidePlayedInLatest = table.Column(nullable: false), - RememberAudioSelections = table.Column(nullable: false), - RememberSubtitleSelections = table.Column(nullable: false), - EnableNextEpisodeAutoPlay = table.Column(nullable: false), - EnableAutoLogin = table.Column(nullable: false), - EnableUserPreferenceAccess = table.Column(nullable: false), - MaxParentalAgeRating = table.Column(nullable: true), - RemoteClientBitrateLimit = table.Column(nullable: true), - InternalId = table.Column(nullable: false), - ProfileImageId = table.Column(nullable: true), - SyncPlayAccess = table.Column(nullable: false), - RowVersion = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - table.ForeignKey( - name: "FK_Users_ImageInfos_ProfileImageId", - column: x => x.ProfileImageId, - principalSchema: "jellyfin", - principalTable: "ImageInfos", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "AccessSchedules", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - UserId = table.Column(nullable: false), - DayOfWeek = table.Column(nullable: false), - StartHour = table.Column(nullable: false), - EndHour = table.Column(nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_AccessSchedules", x => x.Id); - table.ForeignKey( - name: "FK_AccessSchedules_Users_UserId", - column: x => x.UserId, - principalSchema: "jellyfin", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Cascade); - }); - - migrationBuilder.CreateTable( - name: "Permissions", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Kind = table.Column(nullable: false), - Value = table.Column(nullable: false), - RowVersion = table.Column(nullable: false), - Permission_Permissions_Guid = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Permissions", x => x.Id); - table.ForeignKey( - name: "FK_Permissions_Users_Permission_Permissions_Guid", - column: x => x.Permission_Permissions_Guid, - principalSchema: "jellyfin", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateTable( - name: "Preferences", - schema: "jellyfin", - columns: table => new - { - Id = table.Column(nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Kind = table.Column(nullable: false), - Value = table.Column(maxLength: 65535, nullable: false), - RowVersion = table.Column(nullable: false), - Preference_Preferences_Guid = table.Column(nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Preferences", x => x.Id); - table.ForeignKey( - name: "FK_Preferences_Users_Preference_Preferences_Guid", - column: x => x.Preference_Preferences_Guid, - principalSchema: "jellyfin", - principalTable: "Users", - principalColumn: "Id", - onDelete: ReferentialAction.Restrict); - }); - - migrationBuilder.CreateIndex( - name: "IX_AccessSchedules_UserId", - schema: "jellyfin", - table: "AccessSchedules", - column: "UserId"); - - migrationBuilder.CreateIndex( - name: "IX_Permissions_Permission_Permissions_Guid", - schema: "jellyfin", - table: "Permissions", - column: "Permission_Permissions_Guid"); - - migrationBuilder.CreateIndex( - name: "IX_Preferences_Preference_Preferences_Guid", - schema: "jellyfin", - table: "Preferences", - column: "Preference_Preferences_Guid"); - - migrationBuilder.CreateIndex( - name: "IX_Users_ProfileImageId", - schema: "jellyfin", - table: "Users", - column: "ProfileImageId"); - } - - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "AccessSchedules", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Permissions", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Preferences", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "Users", - schema: "jellyfin"); - - migrationBuilder.DropTable( - name: "ImageInfos", - schema: "jellyfin"); - } - } -} diff --git a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs new file mode 100644 index 000000000..6342ce9cf --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.Designer.cs @@ -0,0 +1,312 @@ +#pragma warning disable CS1591 + +// +using System; +using Jellyfin.Server.Implementations; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +namespace Jellyfin.Server.Implementations.Migrations +{ + [DbContext(typeof(JellyfinDb))] + [Migration("20200613202153_AddUsers")] + partial class AddUsers + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("jellyfin") + .HasAnnotation("ProductVersion", "3.1.4"); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DayOfWeek") + .HasColumnType("INTEGER"); + + b.Property("EndHour") + .HasColumnType("REAL"); + + b.Property("StartHour") + .HasColumnType("REAL"); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId"); + + b.ToTable("AccessSchedules"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("DateCreated") + .HasColumnType("TEXT"); + + b.Property("ItemId") + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("LogSeverity") + .HasColumnType("INTEGER"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Overview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("ShortOverview") + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("Type") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(256); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.ToTable("ActivityLogs"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("LastModified") + .HasColumnType("TEXT"); + + b.Property("Path") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(512); + + b.Property("UserId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("UserId") + .IsUnique(); + + b.ToTable("ImageInfos"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Permission_Permissions_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .HasColumnType("INTEGER"); + + b.HasKey("Id"); + + b.HasIndex("Permission_Permissions_Guid"); + + b.ToTable("Permissions"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("INTEGER"); + + b.Property("Kind") + .HasColumnType("INTEGER"); + + b.Property("Preference_Preferences_Guid") + .HasColumnType("TEXT"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("Value") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.HasKey("Id"); + + b.HasIndex("Preference_Preferences_Guid"); + + b.ToTable("Preferences"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.User", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("TEXT"); + + b.Property("AudioLanguagePreference") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("AuthenticationProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("DisplayCollectionsView") + .HasColumnType("INTEGER"); + + b.Property("DisplayMissingEpisodes") + .HasColumnType("INTEGER"); + + b.Property("EasyPassword") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("EnableAutoLogin") + .HasColumnType("INTEGER"); + + b.Property("EnableLocalPassword") + .HasColumnType("INTEGER"); + + b.Property("EnableNextEpisodeAutoPlay") + .HasColumnType("INTEGER"); + + b.Property("EnableUserPreferenceAccess") + .HasColumnType("INTEGER"); + + b.Property("HidePlayedInLatest") + .HasColumnType("INTEGER"); + + b.Property("InternalId") + .HasColumnType("INTEGER"); + + b.Property("InvalidLoginAttemptCount") + .HasColumnType("INTEGER"); + + b.Property("LastActivityDate") + .HasColumnType("TEXT"); + + b.Property("LastLoginDate") + .HasColumnType("TEXT"); + + b.Property("LoginAttemptsBeforeLockout") + .HasColumnType("INTEGER"); + + b.Property("MaxParentalAgeRating") + .HasColumnType("INTEGER"); + + b.Property("MustUpdatePassword") + .HasColumnType("INTEGER"); + + b.Property("Password") + .HasColumnType("TEXT") + .HasMaxLength(65535); + + b.Property("PasswordResetProviderId") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("PlayDefaultAudioTrack") + .HasColumnType("INTEGER"); + + b.Property("RememberAudioSelections") + .HasColumnType("INTEGER"); + + b.Property("RememberSubtitleSelections") + .HasColumnType("INTEGER"); + + b.Property("RemoteClientBitrateLimit") + .HasColumnType("INTEGER"); + + b.Property("RowVersion") + .IsConcurrencyToken() + .HasColumnType("INTEGER"); + + b.Property("SubtitleLanguagePreference") + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.Property("SubtitleMode") + .HasColumnType("INTEGER"); + + b.Property("SyncPlayAccess") + .HasColumnType("INTEGER"); + + b.Property("Username") + .IsRequired() + .HasColumnType("TEXT") + .HasMaxLength(255); + + b.HasKey("Id"); + + b.ToTable("Users"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("AccessSchedules") + .HasForeignKey("UserId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired(); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("ProfileImage") + .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Permissions") + .HasForeignKey("Permission_Permissions_Guid"); + }); + + modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithMany("Preferences") + .HasForeignKey("Preference_Preferences_Guid"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs new file mode 100644 index 000000000..7e5a76850 --- /dev/null +++ b/Jellyfin.Server.Implementations/Migrations/20200613202153_AddUsers.cs @@ -0,0 +1,197 @@ +#pragma warning disable CS1591 +#pragma warning disable SA1601 + +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace Jellyfin.Server.Implementations.Migrations +{ + public partial class AddUsers : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Users", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false), + Username = table.Column(maxLength: 255, nullable: false), + Password = table.Column(maxLength: 65535, nullable: true), + EasyPassword = table.Column(maxLength: 65535, nullable: true), + MustUpdatePassword = table.Column(nullable: false), + AudioLanguagePreference = table.Column(maxLength: 255, nullable: true), + AuthenticationProviderId = table.Column(maxLength: 255, nullable: false), + PasswordResetProviderId = table.Column(maxLength: 255, nullable: false), + InvalidLoginAttemptCount = table.Column(nullable: false), + LastActivityDate = table.Column(nullable: true), + LastLoginDate = table.Column(nullable: true), + LoginAttemptsBeforeLockout = table.Column(nullable: true), + SubtitleMode = table.Column(nullable: false), + PlayDefaultAudioTrack = table.Column(nullable: false), + SubtitleLanguagePreference = table.Column(maxLength: 255, nullable: true), + DisplayMissingEpisodes = table.Column(nullable: false), + DisplayCollectionsView = table.Column(nullable: false), + EnableLocalPassword = table.Column(nullable: false), + HidePlayedInLatest = table.Column(nullable: false), + RememberAudioSelections = table.Column(nullable: false), + RememberSubtitleSelections = table.Column(nullable: false), + EnableNextEpisodeAutoPlay = table.Column(nullable: false), + EnableAutoLogin = table.Column(nullable: false), + EnableUserPreferenceAccess = table.Column(nullable: false), + MaxParentalAgeRating = table.Column(nullable: true), + RemoteClientBitrateLimit = table.Column(nullable: true), + InternalId = table.Column(nullable: false), + SyncPlayAccess = table.Column(nullable: false), + RowVersion = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_Users", x => x.Id); + }); + + migrationBuilder.CreateTable( + name: "AccessSchedules", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(nullable: false), + DayOfWeek = table.Column(nullable: false), + StartHour = table.Column(nullable: false), + EndHour = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_AccessSchedules", x => x.Id); + table.ForeignKey( + name: "FK_AccessSchedules_Users_UserId", + column: x => x.UserId, + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Cascade); + }); + + migrationBuilder.CreateTable( + name: "ImageInfos", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + UserId = table.Column(nullable: true), + Path = table.Column(maxLength: 512, nullable: false), + LastModified = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_ImageInfos", x => x.Id); + table.ForeignKey( + name: "FK_ImageInfos_Users_UserId", + column: x => x.UserId, + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Permissions", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Kind = table.Column(nullable: false), + Value = table.Column(nullable: false), + RowVersion = table.Column(nullable: false), + Permission_Permissions_Guid = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Permissions", x => x.Id); + table.ForeignKey( + name: "FK_Permissions_Users_Permission_Permissions_Guid", + column: x => x.Permission_Permissions_Guid, + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateTable( + name: "Preferences", + schema: "jellyfin", + columns: table => new + { + Id = table.Column(nullable: false) + .Annotation("Sqlite:Autoincrement", true), + Kind = table.Column(nullable: false), + Value = table.Column(maxLength: 65535, nullable: false), + RowVersion = table.Column(nullable: false), + Preference_Preferences_Guid = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Preferences", x => x.Id); + table.ForeignKey( + name: "FK_Preferences_Users_Preference_Preferences_Guid", + column: x => x.Preference_Preferences_Guid, + principalSchema: "jellyfin", + principalTable: "Users", + principalColumn: "Id", + onDelete: ReferentialAction.Restrict); + }); + + migrationBuilder.CreateIndex( + name: "IX_AccessSchedules_UserId", + schema: "jellyfin", + table: "AccessSchedules", + column: "UserId"); + + migrationBuilder.CreateIndex( + name: "IX_ImageInfos_UserId", + schema: "jellyfin", + table: "ImageInfos", + column: "UserId", + unique: true); + + migrationBuilder.CreateIndex( + name: "IX_Permissions_Permission_Permissions_Guid", + schema: "jellyfin", + table: "Permissions", + column: "Permission_Permissions_Guid"); + + migrationBuilder.CreateIndex( + name: "IX_Preferences_Preference_Preferences_Guid", + schema: "jellyfin", + table: "Preferences", + column: "Preference_Preferences_Guid"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "AccessSchedules", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "ImageInfos", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Permissions", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Preferences", + schema: "jellyfin"); + + migrationBuilder.DropTable( + name: "Users", + schema: "jellyfin"); + } + } +} diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs index 9548c38a3..51fad8224 100644 --- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs +++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs @@ -102,8 +102,14 @@ namespace Jellyfin.Server.Implementations.Migrations .HasColumnType("TEXT") .HasMaxLength(512); + b.Property("UserId") + .HasColumnType("TEXT"); + b.HasKey("Id"); + b.HasIndex("UserId") + .IsUnique(); + b.ToTable("ImageInfos"); }); @@ -234,9 +240,6 @@ namespace Jellyfin.Server.Implementations.Migrations b.Property("PlayDefaultAudioTrack") .HasColumnType("INTEGER"); - b.Property("ProfileImageId") - .HasColumnType("INTEGER"); - b.Property("RememberAudioSelections") .HasColumnType("INTEGER"); @@ -267,8 +270,6 @@ namespace Jellyfin.Server.Implementations.Migrations b.HasKey("Id"); - b.HasIndex("ProfileImageId"); - b.ToTable("Users"); }); @@ -281,6 +282,13 @@ namespace Jellyfin.Server.Implementations.Migrations .IsRequired(); }); + modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b => + { + b.HasOne("Jellyfin.Data.Entities.User", null) + .WithOne("ProfileImage") + .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId"); + }); + modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b => { b.HasOne("Jellyfin.Data.Entities.User", null) @@ -294,13 +302,6 @@ namespace Jellyfin.Server.Implementations.Migrations .WithMany("Preferences") .HasForeignKey("Preference_Preferences_Guid"); }); - - modelBuilder.Entity("Jellyfin.Data.Entities.User", b => - { - b.HasOne("Jellyfin.Data.Entities.ImageInfo", "ProfileImage") - .WithMany() - .HasForeignKey("ProfileImageId"); - }); #pragma warning restore 612, 618 } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index bab6cf3fc..63b8e1d94 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -671,7 +671,7 @@ namespace Jellyfin.Server.Implementations.Users public void ClearProfileImage(User user) { var dbContext = _dbProvider.CreateContext(); - dbContext.ImageInfos.Remove(user.ProfileImage); + dbContext.Remove(user.ProfileImage); dbContext.SaveChanges(); } -- cgit v1.2.3 From 8405ae9b1f2dd0414737c9db9d5d709890fec30c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 13 Jun 2020 18:23:13 -0400 Subject: Actually set `BlockUnratedItems` --- Jellyfin.Server.Implementations/Users/UserManager.cs | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 63b8e1d94..123bf2740 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -657,6 +657,10 @@ namespace Jellyfin.Server.Implementations.Users user.AccessSchedules.Add(policyAccessSchedule); } + // TODO: fix this at some point + user.SetPreference( + PreferenceKind.BlockUnratedItems, + policy.BlockUnratedItems?.Select(i => i.ToString()).ToArray() ?? Array.Empty()); user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); -- cgit v1.2.3 From 6f325fea611160061e47e7108d1645514d7b0305 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 13 Jun 2020 18:26:46 -0400 Subject: Add missing properties to UserDto --- Jellyfin.Server.Implementations/Users/UserManager.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 123bf2740..49f6dcdfd 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -20,6 +20,7 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Users; using Microsoft.Extensions.Logging; @@ -339,7 +340,10 @@ namespace Jellyfin.Server.Implementations.Users EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices), EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders), EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders), - SyncPlayAccess = user.SyncPlayAccess + SyncPlayAccess = user.SyncPlayAccess, + BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels), + BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders), + BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems).Select(Enum.Parse).ToArray() } }; } -- cgit v1.2.3 From e8f8307521232e3e3c5cbcf747d1b5aba0c50919 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 13 Jun 2020 19:30:45 -0400 Subject: Add comment --- Jellyfin.Server.Implementations/Users/UserManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 49f6dcdfd..904114758 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -20,7 +20,6 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; using MediaBrowser.Model.Events; using MediaBrowser.Model.Users; using Microsoft.Extensions.Logging; @@ -618,6 +617,8 @@ namespace Jellyfin.Server.Implementations.Users { var dbContext = _dbProvider.CreateContext(); var user = dbContext.Users.Find(userId) ?? throw new ArgumentException("No user exists with given Id!"); + + // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0" int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch { -1 => null, -- cgit v1.2.3 From 44a8ea6bee3cf3fefc3d290dbaaa8c8e1554868f Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 24 Jun 2020 09:45:11 -0600 Subject: implement ChangeEasyPassword from legacy provider --- Jellyfin.Server.Implementations/Users/UserManager.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 97ea03596..55218c5de 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -263,7 +263,17 @@ namespace Jellyfin.Server.Implementations.Users /// public void ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1) { - user.EasyPassword = _cryptoProvider.CreatePasswordHash(newPassword).ToString(); + if (newPassword != null) + { + newPasswordSha1 = _cryptoProvider.CreatePasswordHash(newPassword).ToString(); + } + + if (string.IsNullOrWhiteSpace(newPasswordSha1)) + { + throw new ArgumentNullException(nameof(newPasswordSha1)); + } + + user.EasyPassword = newPasswordSha1; UpdateUser(user); OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); -- cgit v1.2.3 From 9a01cd8590ffcae8ce561e6f733bf59fe54932fa Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 24 Jun 2020 20:19:47 -0400 Subject: Fix user deletion. --- Jellyfin.Server.Implementations/Users/UserManager.cs | 19 ++++++++++++++----- MediaBrowser.Api/UserService.cs | 11 ++--------- MediaBrowser.Controller/Library/IUserManager.cs | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 904114758..d86b25f81 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -192,15 +192,15 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void DeleteUser(User user) + public void DeleteUser(Guid userId) { + var dbContext = _dbProvider.CreateContext(); + var user = dbContext.Users.Find(userId); if (user == null) { - throw new ArgumentNullException(nameof(user)); + throw new ArgumentNullException(nameof(userId)); } - var dbContext = _dbProvider.CreateContext(); - if (dbContext.Users.Find(user.Id) == null) { throw new ArgumentException(string.Format( @@ -226,9 +226,18 @@ namespace Jellyfin.Server.Implementations.Users CultureInfo.InvariantCulture, "The user '{0}' cannot be deleted because there must be at least one admin user in the system.", user.Username), - nameof(user)); + nameof(userId)); + } + + // Clear all entities related to the user from the database. + if (user.ProfileImage != null) + { + dbContext.Remove(user.ProfileImage); } + dbContext.RemoveRange(user.Permissions); + dbContext.RemoveRange(user.Preferences); + dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); dbContext.SaveChanges(); OnUserDeleted?.Invoke(this, new GenericEventArgs(user)); diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 131def554..6e9d788dc 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -365,15 +365,8 @@ namespace MediaBrowser.Api public Task DeleteAsync(DeleteUser request) { - var user = _userManager.GetUserById(request.Id); - - if (user == null) - { - throw new ResourceNotFoundException("User not found"); - } - - _sessionMananger.RevokeUserTokens(user.Id, null); - _userManager.DeleteUser(user); + _userManager.DeleteUser(request.Id); + _sessionMananger.RevokeUserTokens(request.Id, null); return Task.CompletedTask; } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index fe3e4f9e6..4ff3cee0f 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -111,8 +111,8 @@ namespace MediaBrowser.Controller.Library /// /// Deletes the specified user. /// - /// The user to be deleted. - void DeleteUser(User user); + /// The if of the user to be deleted. + void DeleteUser(Guid userId); /// /// Resets the password. -- cgit v1.2.3 From 2967dd6afd158fc278697fa593852de45e99b84a Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 24 Jun 2020 20:36:58 -0400 Subject: Change to ResourceNotFoundException --- Jellyfin.Server.Implementations/Users/UserManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d86b25f81..ae5c311bf 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -12,6 +12,7 @@ using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; +using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Drawing; @@ -198,7 +199,7 @@ namespace Jellyfin.Server.Implementations.Users var user = dbContext.Users.Find(userId); if (user == null) { - throw new ArgumentNullException(nameof(userId)); + throw new ResourceNotFoundException(nameof(userId)); } if (dbContext.Users.Find(user.Id) == null) -- cgit v1.2.3 From c0bd10879aeaeda9d1a62766c5cfd046cc3ff908 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 9 Jul 2020 21:55:07 -0400 Subject: Ignore casing when authenticating users --- Jellyfin.Server.Implementations/Users/UserManager.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index ace9c4af0..47d514b1a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -110,9 +110,8 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(name)); } - // This can't use an overload with StringComparer because that would cause the query to - // have to be evaluated client-side. - return _dbProvider.CreateContext().Users.FirstOrDefault(u => string.Equals(u.Username, name)); + return _dbProvider.CreateContext().Users.ToList() + .FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase)); } /// -- cgit v1.2.3 From 8959621da7d9e0696e530448fc028c0089c2609a Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 12 Jul 2020 14:45:52 -0400 Subject: Fix EF Core memory leak --- .../Users/UserManager.cs | 54 ++++++++++++++++------ 1 file changed, 40 insertions(+), 14 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 47d514b1a..c8d7fa769 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -23,6 +23,7 @@ using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Events; using MediaBrowser.Model.Users; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; namespace Jellyfin.Server.Implementations.Users @@ -86,7 +87,18 @@ namespace Jellyfin.Server.Implementations.Users public event EventHandler>? OnUserLockedOut; /// - public IEnumerable Users => _dbProvider.CreateContext().Users; + public IEnumerable Users + { + get + { + using var dbContext = _dbProvider.CreateContext(); + return dbContext.Users.Include(user => user.Permissions) + .Include(user => user.Preferences) + .Include(user => user.AccessSchedules) + .Include(user => user.ProfileImage) + .ToList(); + } + } /// public IEnumerable UsersIds => _dbProvider.CreateContext().Users.Select(u => u.Id); @@ -99,7 +111,12 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Guid can't be empty", nameof(id)); } - return _dbProvider.CreateContext().Users.Find(id); + using var dbContext = _dbProvider.CreateContext(); + return dbContext.Users.Include(user => user.Permissions) + .Include(user => user.Preferences) + .Include(user => user.AccessSchedules) + .Include(user => user.ProfileImage) + .FirstOrDefault(user => user.Id == id); } /// @@ -110,7 +127,13 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(name)); } - return _dbProvider.CreateContext().Users.ToList() + using var dbContext = _dbProvider.CreateContext(); + + return dbContext.Users.Include(user => user.Permissions) + .Include(user => user.Preferences) + .Include(user => user.AccessSchedules) + .Include(user => user.ProfileImage) + .ToList() .FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase)); } @@ -127,7 +150,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(newName)); } - if (user.Username.Equals(newName, StringComparison.Ordinal)) + if (user.Username.Equals(newName, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("The new and old names must be different."); } @@ -149,7 +172,7 @@ namespace Jellyfin.Server.Implementations.Users /// public void UpdateUser(User user) { - var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateContext(); dbContext.Users.Update(user); dbContext.SaveChanges(); } @@ -157,7 +180,7 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task UpdateUserAsync(User user) { - var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); dbContext.Users.Update(user); await dbContext.SaveChangesAsync().ConfigureAwait(false); @@ -171,7 +194,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)"); } - var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateContext(); // TODO: Remove after user item data is migrated. var max = dbContext.Users.Any() ? dbContext.Users.Select(u => u.InternalId).Max() : 0; @@ -194,8 +217,12 @@ namespace Jellyfin.Server.Implementations.Users /// public void DeleteUser(Guid userId) { - var dbContext = _dbProvider.CreateContext(); - var user = dbContext.Users.Find(userId); + using var dbContext = _dbProvider.CreateContext(); + var user = dbContext.Users.Include(u => u.Permissions) + .Include(u => u.Preferences) + .Include(u => u.AccessSchedules) + .Include(u => u.ProfileImage) + .FirstOrDefault(u => u.Id == userId); if (user == null) { throw new ResourceNotFoundException(nameof(userId)); @@ -380,7 +407,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentNullException(nameof(username)); } - var user = Users.ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); + var user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); bool success; IAuthenticationProvider? authenticationProvider; @@ -408,8 +435,7 @@ namespace Jellyfin.Server.Implementations.Users // Search the database for the user again // the authentication provider might have created it - user = Users - .ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); + user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) { @@ -546,7 +572,7 @@ namespace Jellyfin.Server.Implementations.Users public void Initialize() { // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. - var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateContext(); if (dbContext.Users.Any()) { @@ -698,7 +724,7 @@ namespace Jellyfin.Server.Implementations.Users /// public void ClearProfileImage(User user) { - var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateContext(); dbContext.Remove(user.ProfileImage); dbContext.SaveChanges(); } -- cgit v1.2.3 From 0ee55bc1f98283de0926e0e1608e48c71b833934 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sun, 12 Jul 2020 15:08:55 -0400 Subject: Use AsEnumerable instead of ToList --- Jellyfin.Server.Implementations/Users/UserManager.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 47d514b1a..5a2d7774a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -110,7 +110,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(name)); } - return _dbProvider.CreateContext().Users.ToList() + return _dbProvider.CreateContext().Users.AsEnumerable() .FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase)); } @@ -380,7 +380,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentNullException(nameof(username)); } - var user = Users.ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); + var user = Users.AsEnumerable().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); bool success; IAuthenticationProvider? authenticationProvider; @@ -408,8 +408,7 @@ namespace Jellyfin.Server.Implementations.Users // Search the database for the user again // the authentication provider might have created it - user = Users - .ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); + user = Users.AsEnumerable().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) { -- cgit v1.2.3 From b468ae2aea3b42a3073a81c2566dd19a313dc3fa Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Mon, 13 Jul 2020 14:09:20 -0400 Subject: Use AsEnumerable for UserManager.Users --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 4ed0b7501..6283a1bca 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -96,7 +96,7 @@ namespace Jellyfin.Server.Implementations.Users .Include(user => user.Preferences) .Include(user => user.AccessSchedules) .Include(user => user.ProfileImage) - .ToList(); + .AsEnumerable(); } } -- cgit v1.2.3 From 3b085f6a03bfe945e12b104bb042be1d00981cd2 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 17 Jun 2020 10:24:25 -0400 Subject: Remove UserManager.AddParts --- Emby.Server.Implementations/ApplicationHost.cs | 4 ++- .../Users/UserManager.cs | 35 +++++++++++----------- MediaBrowser.Controller/Library/IUserManager.cs | 3 -- 3 files changed, 20 insertions(+), 22 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index f6077400d..908fe4b30 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -547,6 +547,9 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ServerConfigurationManager); + serviceCollection.AddSingleton>>(() => GetExports()); + serviceCollection.AddSingleton>>(() => GetExports()); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); @@ -795,7 +798,6 @@ namespace Emby.Server.Implementations Resolve().AddParts(GetExports()); Resolve().AddParts(GetExports(), GetExports()); - Resolve().AddParts(GetExports(), GetExports()); Resolve().AddParts(GetExports()); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 6283a1bca..d4b91f3c5 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -39,12 +39,11 @@ namespace Jellyfin.Server.Implementations.Users private readonly IApplicationHost _appHost; private readonly IImageProcessor _imageProcessor; private readonly ILogger _logger; - - private IAuthenticationProvider[] _authenticationProviders = null!; - private DefaultAuthenticationProvider _defaultAuthenticationProvider = null!; - private InvalidAuthProvider _invalidAuthProvider = null!; - private IPasswordResetProvider[] _passwordResetProviders = null!; - private DefaultPasswordResetProvider _defaultPasswordResetProvider = null!; + private readonly IReadOnlyCollection _passwordResetProviders; + private readonly IReadOnlyCollection _authenticationProviders; + private readonly InvalidAuthProvider _invalidAuthProvider; + private readonly DefaultAuthenticationProvider _defaultAuthenticationProvider; + private readonly DefaultPasswordResetProvider _defaultPasswordResetProvider; /// /// Initializes a new instance of the class. @@ -55,13 +54,17 @@ namespace Jellyfin.Server.Implementations.Users /// The application host. /// The image processor. /// The logger. + /// A function that returns available password reset providers. + /// A function that returns available authentication providers. public UserManager( JellyfinDbProvider dbProvider, ICryptoProvider cryptoProvider, INetworkManager networkManager, IApplicationHost appHost, IImageProcessor imageProcessor, - ILogger logger) + ILogger logger, + Func> passwordResetProviders, + Func> authenticationProviders) { _dbProvider = dbProvider; _cryptoProvider = cryptoProvider; @@ -69,6 +72,13 @@ namespace Jellyfin.Server.Implementations.Users _appHost = appHost; _imageProcessor = imageProcessor; _logger = logger; + + _passwordResetProviders = passwordResetProviders.Invoke(); + _authenticationProviders = authenticationProviders.Invoke(); + + _invalidAuthProvider = _authenticationProviders.OfType().First(); + _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); + _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); } /// @@ -557,17 +567,6 @@ namespace Jellyfin.Server.Implementations.Users }; } - /// - public void AddParts(IEnumerable authenticationProviders, IEnumerable passwordResetProviders) - { - _authenticationProviders = authenticationProviders.ToArray(); - _passwordResetProviders = passwordResetProviders.ToArray(); - - _invalidAuthProvider = _authenticationProviders.OfType().First(); - _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); - _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); - } - /// public void Initialize() { diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index e73fe7120..88b96ddbf 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Jellyfin.Data.Entities; -using MediaBrowser.Controller.Authentication; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Events; @@ -166,8 +165,6 @@ namespace MediaBrowser.Controller.Library /// true if XXXX, false otherwise. Task RedeemPasswordResetPin(string pin); - void AddParts(IEnumerable authenticationProviders, IEnumerable passwordResetProviders); - NameIdPair[] GetAuthenticationProviders(); NameIdPair[] GetPasswordResetProviders(); -- cgit v1.2.3 From 303c175714db50bf865939fd12c66dcbda229431 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 20 Jun 2020 17:58:09 -0400 Subject: Fix circular dependency --- Emby.Server.Implementations/ApplicationHost.cs | 3 --- .../Users/DefaultPasswordResetProvider.cs | 16 +++++++++------- Jellyfin.Server.Implementations/Users/UserManager.cs | 17 +++++++++-------- 3 files changed, 18 insertions(+), 18 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs index 908fe4b30..1ff14bdb5 100644 --- a/Emby.Server.Implementations/ApplicationHost.cs +++ b/Emby.Server.Implementations/ApplicationHost.cs @@ -547,9 +547,6 @@ namespace Emby.Server.Implementations serviceCollection.AddSingleton(ServerConfigurationManager); - serviceCollection.AddSingleton>>(() => GetExports()); - serviceCollection.AddSingleton>>(() => GetExports()); - serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs index cf5a01f08..007c29643 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs @@ -6,6 +6,7 @@ using System.IO; using System.Security.Cryptography; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using MediaBrowser.Common; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; @@ -23,7 +24,7 @@ namespace Jellyfin.Server.Implementations.Users private const string BaseResetFileName = "passwordreset"; private readonly IJsonSerializer _jsonSerializer; - private readonly IUserManager _userManager; + private readonly IApplicationHost _appHost; private readonly string _passwordResetFileBase; private readonly string _passwordResetFileBaseDir; @@ -33,16 +34,17 @@ namespace Jellyfin.Server.Implementations.Users /// /// The configuration manager. /// The JSON serializer. - /// The user manager. + /// The application host. public DefaultPasswordResetProvider( IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, - IUserManager userManager) + IApplicationHost appHost) { _passwordResetFileBaseDir = configurationManager.ApplicationPaths.ProgramDataPath; _passwordResetFileBase = Path.Combine(_passwordResetFileBaseDir, BaseResetFileName); _jsonSerializer = jsonSerializer; - _userManager = userManager; + _appHost = appHost; + // TODO: Remove the circular dependency on UserManager } /// @@ -54,6 +56,7 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task RedeemPasswordResetPin(string pin) { + var userManager = _appHost.Resolve(); var usersReset = new List(); foreach (var resetFile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*")) { @@ -72,10 +75,10 @@ namespace Jellyfin.Server.Implementations.Users pin.Replace("-", string.Empty, StringComparison.Ordinal), StringComparison.InvariantCultureIgnoreCase)) { - var resetUser = _userManager.GetUserByName(spr.UserName) + var resetUser = userManager.GetUserByName(spr.UserName) ?? throw new ResourceNotFoundException($"User with a username of {spr.UserName} not found"); - await _userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); + await userManager.ChangePassword(resetUser, pin).ConfigureAwait(false); usersReset.Add(resetUser.Username); File.Delete(resetFile); } @@ -121,7 +124,6 @@ namespace Jellyfin.Server.Implementations.Users } user.EasyPassword = pin; - await _userManager.UpdateUserAsync(user).ConfigureAwait(false); return new ForgotPasswordResult { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d4b91f3c5..988b0c430 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -54,17 +54,13 @@ namespace Jellyfin.Server.Implementations.Users /// The application host. /// The image processor. /// The logger. - /// A function that returns available password reset providers. - /// A function that returns available authentication providers. public UserManager( JellyfinDbProvider dbProvider, ICryptoProvider cryptoProvider, INetworkManager networkManager, IApplicationHost appHost, IImageProcessor imageProcessor, - ILogger logger, - Func> passwordResetProviders, - Func> authenticationProviders) + ILogger logger) { _dbProvider = dbProvider; _cryptoProvider = cryptoProvider; @@ -73,8 +69,8 @@ namespace Jellyfin.Server.Implementations.Users _imageProcessor = imageProcessor; _logger = logger; - _passwordResetProviders = passwordResetProviders.Invoke(); - _authenticationProviders = authenticationProviders.Invoke(); + _passwordResetProviders = appHost.GetExports(); + _authenticationProviders = appHost.GetExports(); _invalidAuthProvider = _authenticationProviders.OfType().First(); _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); @@ -537,7 +533,12 @@ namespace Jellyfin.Server.Implementations.Users if (user != null && isInNetwork) { var passwordResetProvider = GetPasswordResetProvider(user); - return await passwordResetProvider.StartForgotPasswordProcess(user, isInNetwork).ConfigureAwait(false); + var result = await passwordResetProvider + .StartForgotPasswordProcess(user, isInNetwork) + .ConfigureAwait(false); + + await UpdateUserAsync(user).ConfigureAwait(false); + return result; } return new ForgotPasswordResult -- cgit v1.2.3 From 340b585234430efa9bc9ba1689aab09852b33358 Mon Sep 17 00:00:00 2001 From: Claus Vium Date: Tue, 14 Jul 2020 12:38:56 +0200 Subject: Use ToList instead of AsEnumerable due to delayed execution --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 6283a1bca..4ed0b7501 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -96,7 +96,7 @@ namespace Jellyfin.Server.Implementations.Users .Include(user => user.Preferences) .Include(user => user.AccessSchedules) .Include(user => user.ProfileImage) - .AsEnumerable(); + .ToList(); } } -- cgit v1.2.3 From e143387cbd339020e33d30b7e001172519d66923 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 14 Jul 2020 06:47:46 -0600 Subject: Fix update user --- .../Users/UserManager.cs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 4ed0b7501..9aff808db 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -92,7 +92,8 @@ namespace Jellyfin.Server.Implementations.Users get { using var dbContext = _dbProvider.CreateContext(); - return dbContext.Users.Include(user => user.Permissions) + return dbContext.Users + .Include(user => user.Permissions) .Include(user => user.Preferences) .Include(user => user.AccessSchedules) .Include(user => user.ProfileImage) @@ -112,7 +113,8 @@ namespace Jellyfin.Server.Implementations.Users } using var dbContext = _dbProvider.CreateContext(); - return dbContext.Users.Include(user => user.Permissions) + return dbContext.Users + .Include(user => user.Permissions) .Include(user => user.Preferences) .Include(user => user.AccessSchedules) .Include(user => user.ProfileImage) @@ -128,8 +130,8 @@ namespace Jellyfin.Server.Implementations.Users } using var dbContext = _dbProvider.CreateContext(); - - return dbContext.Users.Include(user => user.Permissions) + return dbContext.Users + .Include(user => user.Permissions) .Include(user => user.Preferences) .Include(user => user.AccessSchedules) .Include(user => user.ProfileImage) @@ -218,7 +220,8 @@ namespace Jellyfin.Server.Implementations.Users public void DeleteUser(Guid userId) { using var dbContext = _dbProvider.CreateContext(); - var user = dbContext.Users.Include(u => u.Permissions) + var user = dbContext.Users + .Include(u => u.Permissions) .Include(u => u.Preferences) .Include(u => u.AccessSchedules) .Include(u => u.ProfileImage) @@ -635,7 +638,14 @@ namespace Jellyfin.Server.Implementations.Users public void UpdateConfiguration(Guid userId, UserConfiguration config) { var dbContext = _dbProvider.CreateContext(); - var user = dbContext.Users.Find(userId) ?? throw new ArgumentException("No user exists with given Id!"); + var user = dbContext.Users + .Include(u => u.Permissions) + .Include(u => u.Preferences) + .Include(u => u.AccessSchedules) + .Include(u => u.ProfileImage) + .FirstOrDefault(u => u.Id == userId) + ?? throw new ArgumentException("No user exists with given Id!"); + user.SubtitleMode = config.SubtitleMode; user.HidePlayedInLatest = config.HidePlayedInLatest; user.EnableLocalPassword = config.EnableLocalPassword; -- cgit v1.2.3 From 2f38d9700cd6f6e80976d2bfe320d1a829a8eed4 Mon Sep 17 00:00:00 2001 From: crobibero Date: Sat, 18 Jul 2020 20:29:27 -0600 Subject: fix UserManager UpdatePolicy --- Jellyfin.Server.Implementations/Users/UserManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 9aff808db..e3a7d5467 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -671,7 +671,13 @@ namespace Jellyfin.Server.Implementations.Users public void UpdatePolicy(Guid userId, UserPolicy policy) { var dbContext = _dbProvider.CreateContext(); - var user = dbContext.Users.Find(userId) ?? throw new ArgumentException("No user exists with given Id!"); + var user = dbContext.Users + .Include(u => u.Permissions) + .Include(u => u.Preferences) + .Include(u => u.AccessSchedules) + .Include(u => u.ProfileImage) + .FirstOrDefault(u => u.Id == userId) + ?? throw new ArgumentException("No user exists with given Id!"); // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0" int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch -- cgit v1.2.3 From d1e351a00ac47c2caa7e7e1a22e20c65d27cec7f Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 19 Jul 2020 20:21:30 -0600 Subject: Fix username case change --- Jellyfin.Server.Implementations/Users/UserManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index e3a7d5467..343f452c7 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -152,12 +152,12 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(newName)); } - if (user.Username.Equals(newName, StringComparison.OrdinalIgnoreCase)) + if (user.Username.Equals(newName, StringComparison.Ordinal)) { throw new ArgumentException("The new and old names must be different."); } - if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase))) + if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.Ordinal))) { throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, -- cgit v1.2.3 From 0c5ac10a68ff0d968579b67d48b0bc00580f6bf4 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 21 Jul 2020 14:25:52 -0400 Subject: Make IncrementInvalidLoginAttemptCount async. --- Jellyfin.Server.Implementations/Users/UserManager.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 343f452c7..52e09a55a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -512,7 +512,7 @@ namespace Jellyfin.Server.Implementations.Users } else { - IncrementInvalidLoginAttemptCount(user); + await IncrementInvalidLoginAttemptCount(user).ConfigureAwait(false); _logger.LogInformation( "Authentication request for {UserName} has been denied (IP: {IP}).", user.Username, @@ -882,7 +882,7 @@ namespace Jellyfin.Server.Implementations.Users } } - private void IncrementInvalidLoginAttemptCount(User user) + private async Task IncrementInvalidLoginAttemptCount(User user) { user.InvalidLoginAttemptCount++; int? maxInvalidLogins = user.LoginAttemptsBeforeLockout; @@ -896,7 +896,7 @@ namespace Jellyfin.Server.Implementations.Users user.InvalidLoginAttemptCount); } - UpdateUser(user); + await UpdateUserAsync(user).ConfigureAwait(false); } } } -- cgit v1.2.3 From b4532ad3a2ea0066fa901bd46b6236aac46bceb7 Mon Sep 17 00:00:00 2001 From: crobibero Date: Wed, 22 Jul 2020 11:35:31 -0600 Subject: add missing using --- Jellyfin.Server.Implementations/Users/UserManager.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 343f452c7..38468ce42 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -102,7 +102,16 @@ namespace Jellyfin.Server.Implementations.Users } /// - public IEnumerable UsersIds => _dbProvider.CreateContext().Users.Select(u => u.Id); + public IEnumerable UsersIds + { + get + { + using var dbContext = _dbProvider.CreateContext(); + return dbContext.Users + .Select(user => user.Id) + .ToList(); + } + } /// public User? GetUserById(Guid id) @@ -637,7 +646,7 @@ namespace Jellyfin.Server.Implementations.Users /// public void UpdateConfiguration(Guid userId, UserConfiguration config) { - var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateContext(); var user = dbContext.Users .Include(u => u.Permissions) .Include(u => u.Preferences) @@ -670,7 +679,7 @@ namespace Jellyfin.Server.Implementations.Users /// public void UpdatePolicy(Guid userId, UserPolicy policy) { - var dbContext = _dbProvider.CreateContext(); + using var dbContext = _dbProvider.CreateContext(); var user = dbContext.Users .Include(u => u.Permissions) .Include(u => u.Preferences) -- cgit v1.2.3 From 6cbfae209d5e4621624d9cc249c601f46c3721c5 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 22 Jul 2020 20:57:29 +0200 Subject: Make CreateUser async --- Jellyfin.Api/Controllers/StartupController.cs | 8 ++--- .../Users/UserManager.cs | 42 +++++++++++++--------- MediaBrowser.Api/UserService.cs | 2 +- MediaBrowser.Controller/Library/IUserManager.cs | 4 +-- 4 files changed, 31 insertions(+), 25 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index 04f134b8b..93fb22c4e 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -46,14 +46,12 @@ namespace Jellyfin.Api.Controllers [HttpGet("Configuration")] public StartupConfigurationDto GetStartupConfiguration() { - var result = new StartupConfigurationDto + return new StartupConfigurationDto { UICulture = _config.Configuration.UICulture, MetadataCountryCode = _config.Configuration.MetadataCountryCode, PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage }; - - return result; } /// @@ -92,10 +90,10 @@ namespace Jellyfin.Api.Controllers /// /// The first user. [HttpGet("User")] - public StartupUserDto GetFirstUser() + public async Task GetFirstUser() { // TODO: Remove this method when startup wizard no longer requires an existing user. - _userManager.Initialize(); + await _userManager.InitializeAsync().ConfigureAwait(false); var user = _userManager.Users.First(); return new StartupUserDto { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 343f452c7..5d72ff938 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -188,8 +188,24 @@ namespace Jellyfin.Server.Implementations.Users await dbContext.SaveChangesAsync().ConfigureAwait(false); } + internal async Task CreateUserInternalAsync(string name, JellyfinDb dbContext) + { + // TODO: Remove after user item data is migrated. + var max = await dbContext.Users.AnyAsync().ConfigureAwait(false) + ? await dbContext.Users.Select(u => u.InternalId).MaxAsync().ConfigureAwait(false) + : 0; + + return new User( + name, + _defaultAuthenticationProvider.GetType().FullName, + _defaultPasswordResetProvider.GetType().FullName) + { + InternalId = max + 1 + }; + } + /// - public User CreateUser(string name) + public async Task CreateUserAsync(string name) { if (!IsValidUsername(name)) { @@ -198,18 +214,10 @@ namespace Jellyfin.Server.Implementations.Users using var dbContext = _dbProvider.CreateContext(); - // TODO: Remove after user item data is migrated. - var max = dbContext.Users.Any() ? dbContext.Users.Select(u => u.InternalId).Max() : 0; + var newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false); - var newUser = new User( - name, - _defaultAuthenticationProvider.GetType().FullName, - _defaultPasswordResetProvider.GetType().FullName) - { - InternalId = max + 1 - }; - dbContext.Users.Add(newUser); - dbContext.SaveChanges(); + await dbContext.Users.AddAsync(newUser).ConfigureAwait(false); + await dbContext.SaveChangesAsync().ConfigureAwait(false); OnUserCreated?.Invoke(this, new GenericEventArgs(newUser)); @@ -572,12 +580,12 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void Initialize() + public async Task InitializeAsync() { // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. using var dbContext = _dbProvider.CreateContext(); - if (dbContext.Users.Any()) + if (await dbContext.Users.AnyAsync().ConfigureAwait(false)) { return; } @@ -595,13 +603,13 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Provided username is not valid!", defaultName); } - var newUser = CreateUser(defaultName); + var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); newUser.SetPermission(PermissionKind.IsAdministrator, true); newUser.SetPermission(PermissionKind.EnableContentDeletion, true); newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true); - dbContext.Users.Update(newUser); - dbContext.SaveChanges(); + await dbContext.Users.AddAsync(newUser).ConfigureAwait(false); + await dbContext.SaveChangesAsync().ConfigureAwait(false); } /// diff --git a/MediaBrowser.Api/UserService.cs b/MediaBrowser.Api/UserService.cs index 6e9d788dc..440d1840d 100644 --- a/MediaBrowser.Api/UserService.cs +++ b/MediaBrowser.Api/UserService.cs @@ -525,7 +525,7 @@ namespace MediaBrowser.Api /// System.Object. public async Task Post(CreateUserByName request) { - var newUser = _userManager.CreateUser(request.Name); + var newUser = await _userManager.CreateUserAsync(request.Name).ConfigureAwait(false); // no need to authenticate password for new user if (request.Password != null) diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index e73fe7120..a2ff0c1bc 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -55,7 +55,7 @@ namespace MediaBrowser.Controller.Library /// /// Initializes the user manager and ensures that a user exists. /// - void Initialize(); + Task InitializeAsync(); /// /// Gets a user by Id. @@ -106,7 +106,7 @@ namespace MediaBrowser.Controller.Library /// The created user. /// name /// - User CreateUser(string name); + Task CreateUserAsync(string name); /// /// Deletes the specified user. -- cgit v1.2.3 From 941f326c0b786ba8bac02dd745ea998055e6e554 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Wed, 22 Jul 2020 21:07:13 +0200 Subject: Don't AddAsync --- Jellyfin.Server.Implementations/Users/UserManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 5d72ff938..a0dd2a6b3 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -216,7 +216,7 @@ namespace Jellyfin.Server.Implementations.Users var newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false); - await dbContext.Users.AddAsync(newUser).ConfigureAwait(false); + dbContext.Users.Add(newUser); await dbContext.SaveChangesAsync().ConfigureAwait(false); OnUserCreated?.Invoke(this, new GenericEventArgs(newUser)); @@ -608,7 +608,7 @@ namespace Jellyfin.Server.Implementations.Users newUser.SetPermission(PermissionKind.EnableContentDeletion, true); newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true); - await dbContext.Users.AddAsync(newUser).ConfigureAwait(false); + dbContext.Users.Add(newUser); await dbContext.SaveChangesAsync().ConfigureAwait(false); } -- cgit v1.2.3 From 2da2f1b20b7f3ee49eeaab4db4ae19c666551190 Mon Sep 17 00:00:00 2001 From: Odd Stråbø Date: Sat, 25 Jul 2020 22:19:27 +0200 Subject: Allow space in username --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index e49a99fe5..c3cba4fbf 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -767,7 +767,7 @@ namespace Jellyfin.Server.Implementations.Users // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.) - return Regex.IsMatch(name, @"^[\w\-'._@]*$"); + return Regex.IsMatch(name, @"^[\w\ \-'._@]*$"); } private IAuthenticationProvider GetAuthenticationProvider(User user) -- cgit v1.2.3 From 27e12798bc60ce1d27853afdb763681d4dce66cf Mon Sep 17 00:00:00 2001 From: Odd Stråbø Date: Sun, 26 Jul 2020 13:57:22 +0200 Subject: Update comment to include space --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index c3cba4fbf..eaa6a0a81 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -766,7 +766,7 @@ namespace Jellyfin.Server.Implementations.Users { // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness - // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.) + // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), periods (.) and spaces ( ) return Regex.IsMatch(name, @"^[\w\ \-'._@]*$"); } -- cgit v1.2.3 From 3ed9463d25776c3a371872183742703d470f871d Mon Sep 17 00:00:00 2001 From: K900 Date: Tue, 28 Jul 2020 12:03:08 +0300 Subject: Fix #3624 It doesn't really make sense to throw an error when creating the default user, because the error is completely non-actionable. Instead, if the autodetected username is not valid, just fall back to a sane default. --- Jellyfin.Server.Implementations/Users/UserManager.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index eaa6a0a81..11402ee05 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -600,18 +600,13 @@ namespace Jellyfin.Server.Implementations.Users } var defaultName = Environment.UserName; - if (string.IsNullOrWhiteSpace(defaultName)) + if (string.IsNullOrWhiteSpace(defaultName) || !IsValidUsername(defaultName)) { defaultName = "MyJellyfinUser"; } _logger.LogWarning("No users, creating one with username {UserName}", defaultName); - if (!IsValidUsername(defaultName)) - { - throw new ArgumentException("Provided username is not valid!", defaultName); - } - var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); newUser.SetPermission(PermissionKind.IsAdministrator, true); newUser.SetPermission(PermissionKind.EnableContentDeletion, true); -- cgit v1.2.3 From ca1f15af19e26f8f610a7b56cd6b15a6a308a58f Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 13 Aug 2020 20:48:28 -0400 Subject: Move GenericEventArgs to Jellyfin.Data.Events --- Emby.Dlna/PlayTo/PlayToController.cs | 2 +- Emby.Dlna/PlayTo/PlayToManager.cs | 2 +- Emby.Dlna/Ssdp/DeviceDiscovery.cs | 2 +- Emby.Notifications/NotificationEntryPoint.cs | 2 +- .../Activity/ActivityLogEntryPoint.cs | 2 +- .../Configuration/ServerConfigurationManager.cs | 2 +- .../Devices/DeviceManager.cs | 2 +- .../EntryPoints/ExternalPortForwarding.cs | 2 +- .../EntryPoints/LibraryChangedNotifier.cs | 2 +- .../EntryPoints/RecordingNotifier.cs | 9 ++++---- .../EntryPoints/ServerEventNotifier.cs | 2 +- .../HttpServer/HttpListenerHost.cs | 2 +- .../LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../LiveTv/EmbyTV/TimerManager.cs | 2 +- .../LiveTv/LiveTvManager.cs | 2 +- .../ScheduledTasks/ScheduledTaskWorker.cs | 3 +-- .../ScheduledTasks/TaskManager.cs | 2 +- .../Session/SessionManager.cs | 2 +- .../Session/SessionWebSocketListener.cs | 2 +- .../ActivityLogWebSocketListener.cs | 2 +- .../ScheduledTasksWebSocketListener.cs | 2 +- Jellyfin.Data/Events/GenericEventArgs.cs | 26 ++++++++++++++++++++++ .../Activity/ActivityManager.cs | 2 +- .../Users/DeviceAccessEntryPoint.cs | 2 +- .../Users/UserManager.cs | 2 +- MediaBrowser.Controller/Devices/IDeviceManager.cs | 2 +- MediaBrowser.Controller/Library/IUserManager.cs | 2 +- MediaBrowser.Controller/LiveTv/ILiveTvManager.cs | 2 +- MediaBrowser.Controller/Net/IHttpServer.cs | 2 +- .../Providers/IProviderManager.cs | 2 +- MediaBrowser.Controller/Session/ISessionManager.cs | 2 +- MediaBrowser.Model/Activity/IActivityManager.cs | 2 +- MediaBrowser.Model/Dlna/IDeviceDiscovery.cs | 2 +- MediaBrowser.Model/Events/GenericEventArgs.cs | 26 ---------------------- MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs | 2 +- MediaBrowser.Model/Tasks/ITaskManager.cs | 2 +- MediaBrowser.Providers/Manager/ProviderManager.cs | 2 +- 37 files changed, 65 insertions(+), 65 deletions(-) create mode 100644 Jellyfin.Data/Events/GenericEventArgs.cs delete mode 100644 MediaBrowser.Model/Events/GenericEventArgs.cs (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs index 92a93d434..1f0da8d75 100644 --- a/Emby.Dlna/PlayTo/PlayToController.cs +++ b/Emby.Dlna/PlayTo/PlayToController.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Emby.Dlna.Didl; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Drawing; @@ -18,7 +19,6 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Session; using Microsoft.AspNetCore.WebUtilities; diff --git a/Emby.Dlna/PlayTo/PlayToManager.cs b/Emby.Dlna/PlayTo/PlayToManager.cs index 512589e4d..00edff1a6 100644 --- a/Emby.Dlna/PlayTo/PlayToManager.cs +++ b/Emby.Dlna/PlayTo/PlayToManager.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; @@ -16,7 +17,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Session; using Microsoft.Extensions.Logging; diff --git a/Emby.Dlna/Ssdp/DeviceDiscovery.cs b/Emby.Dlna/Ssdp/DeviceDiscovery.cs index 7daac96d1..18ee188fd 100644 --- a/Emby.Dlna/Ssdp/DeviceDiscovery.cs +++ b/Emby.Dlna/Ssdp/DeviceDiscovery.cs @@ -3,9 +3,9 @@ using System; using System.Collections.Generic; using System.Linq; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Events; using Rssdp; using Rssdp.Infrastructure; diff --git a/Emby.Notifications/NotificationEntryPoint.cs b/Emby.Notifications/NotificationEntryPoint.cs index b923fd26c..ded22d26c 100644 --- a/Emby.Notifications/NotificationEntryPoint.cs +++ b/Emby.Notifications/NotificationEntryPoint.cs @@ -4,6 +4,7 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Entities; @@ -13,7 +14,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Notifications; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs index 39b6361b7..75a791686 100644 --- a/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs +++ b/Emby.Server.Implementations/Activity/ActivityLogEntryPoint.cs @@ -2,6 +2,7 @@ using System; using System.Globalization; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; using MediaBrowser.Controller.Authentication; @@ -9,7 +10,6 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Updates; diff --git a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs index a15295fca..f05a30a89 100644 --- a/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/Emby.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -2,11 +2,11 @@ using System; using System.Globalization; using System.IO; using Emby.Server.Implementations.AppBase; +using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/Devices/DeviceManager.cs b/Emby.Server.Implementations/Devices/DeviceManager.cs index cc4b407f5..f98c694c4 100644 --- a/Emby.Server.Implementations/Devices/DeviceManager.cs +++ b/Emby.Server.Implementations/Devices/DeviceManager.cs @@ -7,13 +7,13 @@ using System.IO; using System.Linq; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Data.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Devices; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Session; diff --git a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs index 9fce49425..2e8cc76d2 100644 --- a/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Implementations/EntryPoints/ExternalPortForwarding.cs @@ -7,11 +7,11 @@ using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Plugins; using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Events; using Microsoft.Extensions.Logging; using Mono.Nat; diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs index 1deef7f72..c9d21d963 100644 --- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -15,7 +16,6 @@ using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.EntryPoints diff --git a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs index 632735910..44d2580d6 100644 --- a/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/RecordingNotifier.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Enums; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Plugins; @@ -43,22 +44,22 @@ namespace Emby.Server.Implementations.EntryPoints return Task.CompletedTask; } - private async void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs e) { await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false); } - private async void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private async void OnLiveTvManagerTimerCreated(object sender, GenericEventArgs e) { await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false); } - private async void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private async void OnLiveTvManagerSeriesTimerCancelled(object sender, GenericEventArgs e) { await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false); } - private async void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs e) + private async void OnLiveTvManagerTimerCancelled(object sender, GenericEventArgs e) { await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false); } diff --git a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs index 826d4d8dc..d023591e1 100644 --- a/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs +++ b/Emby.Server.Implementations/EntryPoints/ServerEventNotifier.cs @@ -4,13 +4,13 @@ using System.Globalization; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Updates; using MediaBrowser.Controller; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Updates; diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index dafdd5b7b..fe39bb4b2 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -12,13 +12,13 @@ using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.Services; using Emby.Server.Implementations.SocketSharp; +using Jellyfin.Data.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Services; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 80e09f0a3..09c52d95b 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -13,6 +13,7 @@ using System.Threading.Tasks; using System.Xml; using Emby.Server.Implementations.Library; using Jellyfin.Data.Enums; +using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; @@ -29,7 +30,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.MediaInfo; diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs index 285a59a24..dd479b7d1 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs @@ -5,8 +5,8 @@ using System.Collections.Concurrent; using System.Globalization; using System.Linq; using System.Threading; +using Jellyfin.Data.Events; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Events; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Serialization; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 1b075d86a..ef505c78e 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -10,6 +10,7 @@ using System.Threading.Tasks; using Emby.Server.Implementations.Library; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; @@ -24,7 +25,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Sorting; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.LiveTv; diff --git a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs index 8a900f42c..36faae65d 100644 --- a/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs +++ b/Emby.Server.Implementations/ScheduledTasks/ScheduledTaskWorker.cs @@ -6,11 +6,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Progress; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs index 81096026b..fff52ff88 100644 --- a/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs +++ b/Emby.Server.Implementations/ScheduledTasks/TaskManager.cs @@ -5,8 +5,8 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 862a7296c..6e4124463 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -9,6 +9,7 @@ using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Data.Events; using MediaBrowser.Common.Events; using MediaBrowser.Common.Extensions; using MediaBrowser.Controller; @@ -24,7 +25,6 @@ using MediaBrowser.Controller.Session; using MediaBrowser.Model.Devices; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Library; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; diff --git a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs index 8bebd37dc..1da7a6473 100644 --- a/Emby.Server.Implementations/Session/SessionWebSocketListener.cs +++ b/Emby.Server.Implementations/Session/SessionWebSocketListener.cs @@ -4,9 +4,9 @@ using System.Linq; using System.Net.WebSockets; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Net; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; diff --git a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs index 6395b8d62..849b3b709 100644 --- a/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ActivityLogWebSocketListener.cs @@ -1,8 +1,8 @@ using System; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Events; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.WebSocketListeners diff --git a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs index 12f815ff7..8a966c137 100644 --- a/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs +++ b/Jellyfin.Api/WebSocketListeners/ScheduledTasksWebSocketListener.cs @@ -1,8 +1,8 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Net; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; diff --git a/Jellyfin.Data/Events/GenericEventArgs.cs b/Jellyfin.Data/Events/GenericEventArgs.cs new file mode 100644 index 000000000..7b9a5111e --- /dev/null +++ b/Jellyfin.Data/Events/GenericEventArgs.cs @@ -0,0 +1,26 @@ +using System; + +namespace Jellyfin.Data.Events +{ + /// + /// Provides a generic EventArgs subclass that can hold any kind of object. + /// + /// The type of this event. + public class GenericEventArgs : EventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// The argument. + public GenericEventArgs(T arg) + { + Argument = arg; + } + + /// + /// Gets the argument. + /// + /// The argument. + public T Argument { get; } + } +} diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs index 2deefbe81..09f2611e4 100644 --- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -2,8 +2,8 @@ using System; using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Model.Activity; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; namespace Jellyfin.Server.Implementations.Activity diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs index 140853e52..1fb89c4a6 100644 --- a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs +++ b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs @@ -4,12 +4,12 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Session; -using MediaBrowser.Model.Events; namespace Jellyfin.Server.Implementations.Users { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 11402ee05..267c1c103 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -10,6 +10,7 @@ using System.Text.RegularExpressions; using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; +using Jellyfin.Data.Events; using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Extensions; @@ -21,7 +22,6 @@ using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Users; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 7d279230b..a038d84d8 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -1,7 +1,7 @@ using System; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Model.Devices; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Querying; using MediaBrowser.Model.Session; diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 6685861a9..c8d8375b3 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -2,9 +2,9 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.Library diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index f619b011b..d6f629a1b 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -3,11 +3,11 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Events; using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.Querying; diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index e6609fae3..b04ebda8c 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using MediaBrowser.Model.Events; +using Jellyfin.Data.Events; using MediaBrowser.Model.Services; using Microsoft.AspNetCore.Http; diff --git a/MediaBrowser.Controller/Providers/IProviderManager.cs b/MediaBrowser.Controller/Providers/IProviderManager.cs index 955db0278..ef744ee3c 100644 --- a/MediaBrowser.Controller/Providers/IProviderManager.cs +++ b/MediaBrowser.Controller/Providers/IProviderManager.cs @@ -4,12 +4,12 @@ using System.IO; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Providers; namespace MediaBrowser.Controller.Providers diff --git a/MediaBrowser.Controller/Session/ISessionManager.cs b/MediaBrowser.Controller/Session/ISessionManager.cs index e54f21050..d461a9281 100644 --- a/MediaBrowser.Controller/Session/ISessionManager.cs +++ b/MediaBrowser.Controller/Session/ISessionManager.cs @@ -2,11 +2,11 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using Jellyfin.Data.Events; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Security; using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Events; using MediaBrowser.Model.Session; using MediaBrowser.Model.SyncPlay; diff --git a/MediaBrowser.Model/Activity/IActivityManager.cs b/MediaBrowser.Model/Activity/IActivityManager.cs index 9dab5e77b..2362f7e92 100644 --- a/MediaBrowser.Model/Activity/IActivityManager.cs +++ b/MediaBrowser.Model/Activity/IActivityManager.cs @@ -4,7 +4,7 @@ using System; using System.Linq; using System.Threading.Tasks; using Jellyfin.Data.Entities; -using MediaBrowser.Model.Events; +using Jellyfin.Data.Events; using MediaBrowser.Model.Querying; namespace MediaBrowser.Model.Activity diff --git a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs index 76c9a4b04..05209e53d 100644 --- a/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs +++ b/MediaBrowser.Model/Dlna/IDeviceDiscovery.cs @@ -1,7 +1,7 @@ #pragma warning disable CS1591 using System; -using MediaBrowser.Model.Events; +using Jellyfin.Data.Events; namespace MediaBrowser.Model.Dlna { diff --git a/MediaBrowser.Model/Events/GenericEventArgs.cs b/MediaBrowser.Model/Events/GenericEventArgs.cs deleted file mode 100644 index 347ea2281..000000000 --- a/MediaBrowser.Model/Events/GenericEventArgs.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; - -namespace MediaBrowser.Model.Events -{ - /// - /// Provides a generic EventArgs subclass that can hold any kind of object. - /// - /// The type of this event. - public class GenericEventArgs : EventArgs - { - /// - /// Initializes a new instance of the class. - /// - /// The argument. - public GenericEventArgs(T arg) - { - Argument = arg; - } - - /// - /// Gets the argument. - /// - /// The argument. - public T Argument { get; } - } -} diff --git a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs index b08acba2c..2f05e08c5 100644 --- a/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs +++ b/MediaBrowser.Model/Tasks/IScheduledTaskWorker.cs @@ -1,6 +1,6 @@ #nullable disable using System; -using MediaBrowser.Model.Events; +using Jellyfin.Data.Events; namespace MediaBrowser.Model.Tasks { diff --git a/MediaBrowser.Model/Tasks/ITaskManager.cs b/MediaBrowser.Model/Tasks/ITaskManager.cs index 363773ff7..02b29074e 100644 --- a/MediaBrowser.Model/Tasks/ITaskManager.cs +++ b/MediaBrowser.Model/Tasks/ITaskManager.cs @@ -3,7 +3,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using MediaBrowser.Model.Events; +using Jellyfin.Data.Events; namespace MediaBrowser.Model.Tasks { diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index bbd7166e6..9f63c6046 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -9,6 +9,7 @@ using System.Net.Mime; using System.Threading; using System.Threading.Tasks; using Jellyfin.Data.Entities; +using Jellyfin.Data.Events; using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; @@ -22,7 +23,6 @@ using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Subtitles; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Events; using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Providers; -- cgit v1.2.3 From 816c80525a64fcac441f44f1d508028070fdc21d Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 15 Aug 2020 15:55:15 -0400 Subject: Use IEventManager in UserManager --- .../Users/UserManager.cs | 29 +++++++++------------- MediaBrowser.Controller/Events/IEventManager.cs | 10 +++++++- MediaBrowser.Controller/Library/IUserManager.cs | 20 --------------- 3 files changed, 21 insertions(+), 38 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 267c1c103..3e8edeb44 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -11,12 +11,14 @@ using System.Threading.Tasks; using Jellyfin.Data.Entities; using Jellyfin.Data.Enums; using Jellyfin.Data.Events; +using Jellyfin.Data.Events.Users; using MediaBrowser.Common; using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Drawing; +using MediaBrowser.Controller.Events; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; @@ -34,6 +36,7 @@ namespace Jellyfin.Server.Implementations.Users public class UserManager : IUserManager { private readonly JellyfinDbProvider _dbProvider; + private readonly IEventManager _eventManager; private readonly ICryptoProvider _cryptoProvider; private readonly INetworkManager _networkManager; private readonly IApplicationHost _appHost; @@ -49,6 +52,7 @@ namespace Jellyfin.Server.Implementations.Users /// Initializes a new instance of the class. /// /// The database provider. + /// The event manager. /// The cryptography provider. /// The network manager. /// The application host. @@ -56,6 +60,7 @@ namespace Jellyfin.Server.Implementations.Users /// The logger. public UserManager( JellyfinDbProvider dbProvider, + IEventManager eventManager, ICryptoProvider cryptoProvider, INetworkManager networkManager, IApplicationHost appHost, @@ -63,6 +68,7 @@ namespace Jellyfin.Server.Implementations.Users ILogger logger) { _dbProvider = dbProvider; + _eventManager = eventManager; _cryptoProvider = cryptoProvider; _networkManager = networkManager; _appHost = appHost; @@ -77,21 +83,9 @@ namespace Jellyfin.Server.Implementations.Users _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); } - /// - public event EventHandler>? OnUserPasswordChanged; - /// public event EventHandler>? OnUserUpdated; - /// - public event EventHandler>? OnUserCreated; - - /// - public event EventHandler>? OnUserDeleted; - - /// - public event EventHandler>? OnUserLockedOut; - /// public IEnumerable Users { @@ -234,7 +228,7 @@ namespace Jellyfin.Server.Implementations.Users dbContext.Users.Add(newUser); await dbContext.SaveChangesAsync().ConfigureAwait(false); - OnUserCreated?.Invoke(this, new GenericEventArgs(newUser)); + await _eventManager.PublishAsync(new UserCreatedEventArgs(newUser)).ConfigureAwait(false); return newUser; } @@ -293,7 +287,8 @@ namespace Jellyfin.Server.Implementations.Users dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); dbContext.SaveChanges(); - OnUserDeleted?.Invoke(this, new GenericEventArgs(user)); + + _eventManager.Publish(new UserDeletedEventArgs(user)); } /// @@ -319,7 +314,7 @@ namespace Jellyfin.Server.Implementations.Users await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false); await UpdateUserAsync(user).ConfigureAwait(false); - OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); + await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false); } /// @@ -338,7 +333,7 @@ namespace Jellyfin.Server.Implementations.Users user.EasyPassword = newPasswordSha1; UpdateUser(user); - OnUserPasswordChanged?.Invoke(this, new GenericEventArgs(user)); + _eventManager.Publish(new UserPasswordChangedEventArgs(user)); } /// @@ -901,7 +896,7 @@ namespace Jellyfin.Server.Implementations.Users if (maxInvalidLogins.HasValue && user.InvalidLoginAttemptCount >= maxInvalidLogins) { user.SetPermission(PermissionKind.IsDisabled, true); - OnUserLockedOut?.Invoke(this, new GenericEventArgs(user)); + await _eventManager.PublishAsync(new UserLockedOutEventArgs(user)).ConfigureAwait(false); _logger.LogWarning( "Disabling user {Username} due to {Attempts} unsuccessful login attempts.", user.Username, diff --git a/MediaBrowser.Controller/Events/IEventManager.cs b/MediaBrowser.Controller/Events/IEventManager.cs index 794a8709e..a1f40b3a6 100644 --- a/MediaBrowser.Controller/Events/IEventManager.cs +++ b/MediaBrowser.Controller/Events/IEventManager.cs @@ -11,10 +11,18 @@ namespace MediaBrowser.Controller.Events /// /// Publishes an event. /// + /// the event arguments. + /// The type of event. + void Publish(T eventArgs) + where T : EventArgs; + + /// + /// Publishes an event asynchronously. + /// /// The event arguments. /// The type of event. /// A task representing the publishing of the event. - Task Publish(T eventArgs) + Task PublishAsync(T eventArgs) where T : EventArgs; } } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index c8d8375b3..96a41920a 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -19,26 +19,6 @@ namespace MediaBrowser.Controller.Library /// event EventHandler> OnUserUpdated; - /// - /// Occurs when a user is created. - /// - event EventHandler> OnUserCreated; - - /// - /// Occurs when a user is deleted. - /// - event EventHandler> OnUserDeleted; - - /// - /// Occurs when a user's password is changed. - /// - event EventHandler> OnUserPasswordChanged; - - /// - /// Occurs when a user is locked out. - /// - event EventHandler> OnUserLockedOut; - /// /// Gets the users. /// -- cgit v1.2.3 From ec8967b8e6f201db84139c6ea96cc1769c2416ff Mon Sep 17 00:00:00 2001 From: crobibero Date: Thu, 27 Aug 2020 10:00:06 -0600 Subject: Fix partial library and channel access --- Jellyfin.Server.Implementations/Users/UserManager.cs | 12 ++++++------ Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 6 ++++-- MediaBrowser.Model/Users/UserPolicy.cs | 12 ++++++------ 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 3e8edeb44..8f04baa08 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -402,13 +402,13 @@ namespace Jellyfin.Server.Implementations.Users EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing), AccessSchedules = user.AccessSchedules.ToArray(), BlockedTags = user.GetPreference(PreferenceKind.BlockedTags), - EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels), + EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels)?.Select(Guid.Parse).ToArray(), EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices), - EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders), + EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders)?.Select(Guid.Parse).ToArray(), EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders), SyncPlayAccess = user.SyncPlayAccess, - BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels), - BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders), + BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels)?.Select(Guid.Parse).ToArray(), + BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders)?.Select(Guid.Parse).ToArray(), BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems).Select(Enum.Parse).ToArray() } }; @@ -735,9 +735,9 @@ namespace Jellyfin.Server.Implementations.Users PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems?.Select(i => i.ToString()).ToArray() ?? Array.Empty()); user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); - user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); + user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); - user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); + user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); dbContext.Update(user); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index ac6890f38..74c550331 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -1,5 +1,7 @@ using System; +using System.Globalization; using System.IO; +using System.Linq; using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Serialization; using Jellyfin.Data.Entities; @@ -166,9 +168,9 @@ namespace Jellyfin.Server.Migrations.Routines } user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); - user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); + user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); - user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); + user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews); user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders); diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index caf2e0f54..a1f01f7e8 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -80,11 +80,11 @@ namespace MediaBrowser.Model.Users public bool EnableAllDevices { get; set; } - public string[] EnabledChannels { get; set; } + public Guid[] EnabledChannels { get; set; } public bool EnableAllChannels { get; set; } - public string[] EnabledFolders { get; set; } + public Guid[] EnabledFolders { get; set; } public bool EnableAllFolders { get; set; } @@ -94,9 +94,9 @@ namespace MediaBrowser.Model.Users public bool EnablePublicSharing { get; set; } - public string[] BlockedMediaFolders { get; set; } + public Guid[] BlockedMediaFolders { get; set; } - public string[] BlockedChannels { get; set; } + public Guid[] BlockedChannels { get; set; } public int RemoteClientBitrateLimit { get; set; } @@ -145,10 +145,10 @@ namespace MediaBrowser.Model.Users LoginAttemptsBeforeLockout = -1; EnableAllChannels = true; - EnabledChannels = Array.Empty(); + EnabledChannels = Array.Empty(); EnableAllFolders = true; - EnabledFolders = Array.Empty(); + EnabledFolders = Array.Empty(); EnabledDevices = Array.Empty(); EnableAllDevices = true; -- cgit v1.2.3 From 211c9cd60850c6c33d1211cc5a7e35a94b19bab4 Mon Sep 17 00:00:00 2001 From: KonH Date: Sat, 3 Oct 2020 22:03:23 +0700 Subject: Remove unnecessary null checks in some places Related to https://github.com/jellyfin/jellyfin/issues/2149 --- Jellyfin.Api/Helpers/MediaInfoHelper.cs | 2 +- Jellyfin.Api/Helpers/TranscodingJobHelper.cs | 5 +---- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Api/Helpers/MediaInfoHelper.cs b/Jellyfin.Api/Helpers/MediaInfoHelper.cs index 1207fb513..e78f63b25 100644 --- a/Jellyfin.Api/Helpers/MediaInfoHelper.cs +++ b/Jellyfin.Api/Helpers/MediaInfoHelper.cs @@ -554,7 +554,7 @@ namespace Jellyfin.Api.Helpers private long? GetMaxBitrate(long? clientMaxBitrate, User user, string ipAddress) { var maxBitrate = clientMaxBitrate; - var remoteClientMaxBitrate = user?.RemoteClientBitrateLimit ?? 0; + var remoteClientMaxBitrate = user.RemoteClientBitrateLimit ?? 0; if (remoteClientMaxBitrate <= 0) { diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs index 64d1227f7..0db1fabff 100644 --- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs +++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs @@ -740,10 +740,7 @@ namespace Jellyfin.Api.Helpers /// The state. private void OnFfMpegProcessExited(Process process, TranscodingJobDto job, StreamState state) { - if (job != null) - { - job.HasExited = true; - } + job.HasExited = true; _logger.LogDebug("Disposing stream resources"); state.Dispose(); diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 8f04baa08..dfd7ee99d 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -799,7 +799,7 @@ namespace Jellyfin.Server.Implementations.Users private IList GetPasswordResetProviders(User user) { - var passwordResetProviderId = user?.PasswordResetProviderId; + var passwordResetProviderId = user.PasswordResetProviderId; var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray(); if (!string.IsNullOrEmpty(passwordResetProviderId)) -- cgit v1.2.3 From 5a7dda337f4cdda0d0c61adef3d2b13772e708d0 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 11:50:00 -0400 Subject: Add active session tracking Adds a flag for a maximum number of user sessions, as well as an authentication check to ensure that the user is not above this level. --- Emby.Server.Implementations/Session/SessionManager.cs | 13 +++++++++++++ Jellyfin.Data/Entities/User.cs | 5 +++++ Jellyfin.Server.Implementations/Users/UserManager.cs | 2 ++ MediaBrowser.Model/Users/UserPolicy.cs | 6 ++++++ 4 files changed, 26 insertions(+) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index e42d47853..5903d395a 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1484,6 +1484,19 @@ namespace Emby.Server.Implementations.Session throw new SecurityException("User is not allowed access from this device."); } + var sessionsCount = Sessions.Where(i => string.Equals(i.UserId, user.Id)).ToList().Count; + int maxActiveSessions = user.MaxActiveSessions; + _logger.LogDebug("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions); + if (maxActiveSessions >= 0 && sessionsCount >= maxActiveSessions) + { + throw new SecurityException( + "User {User} is at their maximum number of sessions ({Sessions}/{Max}).", + user.Username, + sessionsCount, + maxActiveSessions + ) + } + var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName); var session = LogSessionActivity( diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index f7ab57a1b..daa4de0b5 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -188,6 +188,11 @@ namespace Jellyfin.Data.Entities /// public int? LoginAttemptsBeforeLockout { get; set; } + /// + /// Gets or sets the maximum number of active sessions the user can have at once. + /// + public int? MaxActiveSessions { get; set; } + /// /// Gets or sets the subtitle mode. /// diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 8f04baa08..43698efb7 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -379,6 +379,7 @@ namespace Jellyfin.Server.Implementations.Users PasswordResetProviderId = user.PasswordResetProviderId, InvalidLoginAttemptCount = user.InvalidLoginAttemptCount, LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout ?? -1, + MaxActiveSessions = user.MaxActiveSessions ?? -1, IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator), IsHidden = user.HasPermission(PermissionKind.IsHidden), IsDisabled = user.HasPermission(PermissionKind.IsDisabled), @@ -701,6 +702,7 @@ namespace Jellyfin.Server.Implementations.Users user.PasswordResetProviderId = policy.PasswordResetProviderId; user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount; user.LoginAttemptsBeforeLockout = maxLoginAttempts; + user.MaxActiveSessions = policy.MaxActiveSessions; user.SyncPlayAccess = policy.SyncPlayAccess; user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator); user.SetPermission(PermissionKind.IsHidden, policy.IsHidden); diff --git a/MediaBrowser.Model/Users/UserPolicy.cs b/MediaBrowser.Model/Users/UserPolicy.cs index a1f01f7e8..53dcb6bbd 100644 --- a/MediaBrowser.Model/Users/UserPolicy.cs +++ b/MediaBrowser.Model/Users/UserPolicy.cs @@ -92,6 +92,10 @@ namespace MediaBrowser.Model.Users public int LoginAttemptsBeforeLockout { get; set; } + public int ActiveSessionCount { get; set; } + + public int MaxActiveSessions { get; set; } + public bool EnablePublicSharing { get; set; } public Guid[] BlockedMediaFolders { get; set; } @@ -144,6 +148,8 @@ namespace MediaBrowser.Model.Users LoginAttemptsBeforeLockout = -1; + MaxActiveSessions = -1; + EnableAllChannels = true; EnabledChannels = Array.Empty(); -- cgit v1.2.3 From cd328a0be3a8a3e20c51eb999f1b858d5263bab1 Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Sun, 4 Oct 2020 13:34:53 -0400 Subject: Remove default set for MaxActiveSessions --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 43698efb7..d8ec2a3cd 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -379,7 +379,7 @@ namespace Jellyfin.Server.Implementations.Users PasswordResetProviderId = user.PasswordResetProviderId, InvalidLoginAttemptCount = user.InvalidLoginAttemptCount, LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout ?? -1, - MaxActiveSessions = user.MaxActiveSessions ?? -1, + MaxActiveSessions = user.MaxActiveSessions, IsAdministrator = user.HasPermission(PermissionKind.IsAdministrator), IsHidden = user.HasPermission(PermissionKind.IsHidden), IsDisabled = user.HasPermission(PermissionKind.IsDisabled), -- cgit v1.2.3 From d4a492ef93f6b663fd4a4f7710613f06863f401f Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Mon, 5 Oct 2020 22:51:52 -0400 Subject: Fix activity log query. --- Jellyfin.Server.Implementations/Activity/ActivityManager.cs | 9 ++++++--- .../Jellyfin.Server.Implementations.csproj | 1 + .../Users/DisplayPreferencesManager.cs | 1 + Jellyfin.Server.Implementations/Users/UserManager.cs | 11 ++++++----- 4 files changed, 14 insertions(+), 8 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs index 695e2fbd8..5926abfe0 100644 --- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -45,7 +45,9 @@ namespace Jellyfin.Server.Implementations.Activity { await using var dbContext = _provider.CreateContext(); - IQueryable entries = dbContext.ActivityLogs.OrderByDescending(entry => entry.DateCreated); + IQueryable entries = dbContext.ActivityLogs + .AsQueryable() + .OrderByDescending(entry => entry.DateCreated); if (query.MinDate.HasValue) { @@ -59,10 +61,11 @@ namespace Jellyfin.Server.Implementations.Activity return new QueryResult { - Items = await entries.Skip(query.StartIndex ?? 0) + Items = await entries + .Skip(query.StartIndex ?? 0) .Take(query.Limit ?? 100) + .AsAsyncEnumerable() .Select(ConvertToOldModel) - .AsQueryable() .ToListAsync() .ConfigureAwait(false), TotalRecordCount = await entries.CountAsync().ConfigureAwait(false) diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 4e79dd8d6..17ba09258 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -24,6 +24,7 @@ + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs index 46f1c618f..76f943385 100644 --- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs +++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs @@ -61,6 +61,7 @@ namespace Jellyfin.Server.Implementations.Users public IList ListItemDisplayPreferences(Guid userId, string client) { return _dbContext.ItemDisplayPreferences + .AsQueryable() .Where(prefs => prefs.UserId == userId && prefs.ItemId != Guid.Empty && string.Equals(prefs.Client, client)) .ToList(); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 8f04baa08..8f6a0496a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -108,6 +108,7 @@ namespace Jellyfin.Server.Implementations.Users { using var dbContext = _dbProvider.CreateContext(); return dbContext.Users + .AsQueryable() .Select(user => user.Id) .ToList(); } @@ -200,8 +201,8 @@ namespace Jellyfin.Server.Implementations.Users internal async Task CreateUserInternalAsync(string name, JellyfinDb dbContext) { // TODO: Remove after user item data is migrated. - var max = await dbContext.Users.AnyAsync().ConfigureAwait(false) - ? await dbContext.Users.Select(u => u.InternalId).MaxAsync().ConfigureAwait(false) + var max = await dbContext.Users.AsQueryable().AnyAsync().ConfigureAwait(false) + ? await dbContext.Users.AsQueryable().Select(u => u.InternalId).MaxAsync().ConfigureAwait(false) : 0; return new User( @@ -221,7 +222,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)"); } - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); var newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false); @@ -587,9 +588,9 @@ namespace Jellyfin.Server.Implementations.Users public async Task InitializeAsync() { // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); - if (await dbContext.Users.AnyAsync().ConfigureAwait(false)) + if (await dbContext.Users.AsQueryable().AnyAsync().ConfigureAwait(false)) { return; } -- cgit v1.2.3 From aff8684d2e513d4c73e7919f464e2d34d6d63047 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Mon, 26 Oct 2020 20:31:10 -0400 Subject: Add caching to users --- .../Users/UserManager.cs | 86 ++++++++-------------- 1 file changed, 31 insertions(+), 55 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 437833aa3..c8b754514 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -2,6 +2,7 @@ #pragma warning disable CA1307 using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -48,6 +49,8 @@ namespace Jellyfin.Server.Implementations.Users private readonly DefaultAuthenticationProvider _defaultAuthenticationProvider; private readonly DefaultPasswordResetProvider _defaultPasswordResetProvider; + private readonly IDictionary _users = new ConcurrentDictionary(); + /// /// Initializes a new instance of the class. /// @@ -81,6 +84,17 @@ namespace Jellyfin.Server.Implementations.Users _invalidAuthProvider = _authenticationProviders.OfType().First(); _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); + + using var dbContext = _dbProvider.CreateContext(); + foreach (var user in dbContext.Users + .Include(user => user.Permissions) + .Include(user => user.Preferences) + .Include(user => user.AccessSchedules) + .Include(user => user.ProfileImage) + .AsEnumerable()) + { + _users.Add(user.Id, user); + } } /// @@ -89,29 +103,13 @@ namespace Jellyfin.Server.Implementations.Users /// public IEnumerable Users { - get - { - using var dbContext = _dbProvider.CreateContext(); - return dbContext.Users - .Include(user => user.Permissions) - .Include(user => user.Preferences) - .Include(user => user.AccessSchedules) - .Include(user => user.ProfileImage) - .ToList(); - } + get { return _users.Values; } } /// public IEnumerable UsersIds { - get - { - using var dbContext = _dbProvider.CreateContext(); - return dbContext.Users - .AsQueryable() - .Select(user => user.Id) - .ToList(); - } + get { return _users.Keys; } } /// @@ -122,13 +120,8 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Guid can't be empty", nameof(id)); } - using var dbContext = _dbProvider.CreateContext(); - return dbContext.Users - .Include(user => user.Permissions) - .Include(user => user.Preferences) - .Include(user => user.AccessSchedules) - .Include(user => user.ProfileImage) - .FirstOrDefault(user => user.Id == id); + _users.TryGetValue(id, out var user); + return user; } /// @@ -139,14 +132,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Invalid username", nameof(name)); } - using var dbContext = _dbProvider.CreateContext(); - return dbContext.Users - .Include(user => user.Permissions) - .Include(user => user.Preferences) - .Include(user => user.AccessSchedules) - .Include(user => user.ProfileImage) - .AsEnumerable() - .FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase)); + return _users.Values.FirstOrDefault(u => string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase)); } /// @@ -205,13 +191,17 @@ namespace Jellyfin.Server.Implementations.Users ? await dbContext.Users.AsQueryable().Select(u => u.InternalId).MaxAsync().ConfigureAwait(false) : 0; - return new User( + var user = new User( name, _defaultAuthenticationProvider.GetType().FullName, _defaultPasswordResetProvider.GetType().FullName) { InternalId = max + 1 }; + + _users.Add(user.Id, user); + + return user; } /// @@ -237,28 +227,12 @@ namespace Jellyfin.Server.Implementations.Users /// public void DeleteUser(Guid userId) { - using var dbContext = _dbProvider.CreateContext(); - var user = dbContext.Users - .Include(u => u.Permissions) - .Include(u => u.Preferences) - .Include(u => u.AccessSchedules) - .Include(u => u.ProfileImage) - .FirstOrDefault(u => u.Id == userId); - if (user == null) + if (!_users.TryGetValue(userId, out var user)) { throw new ResourceNotFoundException(nameof(userId)); } - if (dbContext.Users.Find(user.Id) == null) - { - throw new ArgumentException(string.Format( - CultureInfo.InvariantCulture, - "The user cannot be deleted because there is no user with the Name {0} and Id {1}.", - user.Username, - user.Id)); - } - - if (dbContext.Users.Count() == 1) + if (_users.Count == 1) { throw new InvalidOperationException(string.Format( CultureInfo.InvariantCulture, @@ -277,6 +251,8 @@ namespace Jellyfin.Server.Implementations.Users nameof(userId)); } + using var dbContext = _dbProvider.CreateContext(); + // Clear all entities related to the user from the database. if (user.ProfileImage != null) { @@ -288,6 +264,7 @@ namespace Jellyfin.Server.Implementations.Users dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); dbContext.SaveChanges(); + _users.Remove(userId); _eventManager.Publish(new UserDeletedEventArgs(user)); } @@ -589,9 +566,7 @@ namespace Jellyfin.Server.Implementations.Users public async Task InitializeAsync() { // TODO: Refactor the startup wizard so that it doesn't require a user to already exist. - await using var dbContext = _dbProvider.CreateContext(); - - if (await dbContext.Users.AsQueryable().AnyAsync().ConfigureAwait(false)) + if (_users.Any()) { return; } @@ -604,6 +579,7 @@ namespace Jellyfin.Server.Implementations.Users _logger.LogWarning("No users, creating one with username {UserName}", defaultName); + await using var dbContext = _dbProvider.CreateContext(); var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false); newUser.SetPermission(PermissionKind.IsAdministrator, true); newUser.SetPermission(PermissionKind.EnableContentDeletion, true); -- cgit v1.2.3 From f051590f14d38012bd8885c6e5ed0406d3c91ecc Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 27 Oct 2020 12:12:08 -0400 Subject: Apply suggestions --- Jellyfin.Server.Implementations/Users/UserManager.cs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index c8b754514..efc3f439d 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -49,7 +49,7 @@ namespace Jellyfin.Server.Implementations.Users private readonly DefaultAuthenticationProvider _defaultAuthenticationProvider; private readonly DefaultPasswordResetProvider _defaultPasswordResetProvider; - private readonly IDictionary _users = new ConcurrentDictionary(); + private readonly IDictionary _users; /// /// Initializes a new instance of the class. @@ -85,6 +85,7 @@ namespace Jellyfin.Server.Implementations.Users _defaultAuthenticationProvider = _authenticationProviders.OfType().First(); _defaultPasswordResetProvider = _passwordResetProviders.OfType().First(); + _users = new ConcurrentDictionary(); using var dbContext = _dbProvider.CreateContext(); foreach (var user in dbContext.Users .Include(user => user.Permissions) @@ -101,16 +102,10 @@ namespace Jellyfin.Server.Implementations.Users public event EventHandler>? OnUserUpdated; /// - public IEnumerable Users - { - get { return _users.Values; } - } + public IEnumerable Users => _users.Values; /// - public IEnumerable UsersIds - { - get { return _users.Keys; } - } + public IEnumerable UsersIds => _users.Keys; /// public User? GetUserById(Guid id) -- cgit v1.2.3 From 67f39ed54fe4182783efcbbee6338455ceecf30b Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 27 Oct 2020 12:28:37 -0400 Subject: Properly remove profile images --- Jellyfin.Server.Implementations/Users/UserManager.cs | 1 + 1 file changed, 1 insertion(+) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index efc3f439d..b39333b5c 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -724,6 +724,7 @@ namespace Jellyfin.Server.Implementations.Users using var dbContext = _dbProvider.CreateContext(); dbContext.Remove(user.ProfileImage); dbContext.SaveChanges(); + user.ProfileImage = null; } private static bool IsValidUsername(string name) -- cgit v1.2.3 From d887e424052e3f57b7578d9dc7f62bcd8085fcbe Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 29 Oct 2020 19:16:39 -0400 Subject: Fix possible NullReferenceException --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 437833aa3..5f25c7737 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -460,7 +460,7 @@ namespace Jellyfin.Server.Implementations.Users // the authentication provider might have created it user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); - if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy) + if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy && user != null) { UpdatePolicy(user.Id, hasNewUserPolicy.GetNewUserPolicy()); -- cgit v1.2.3 From 72263613d039ccfb70b70bae9f53da53bc8757c4 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 29 Oct 2020 20:30:33 -0400 Subject: Convert some code in UserManager to async --- Jellyfin.Api/Controllers/ImageController.cs | 6 +++--- Jellyfin.Api/Controllers/UserController.cs | 18 +++++++----------- .../Users/UserManager.cs | 22 ++++++++++------------ MediaBrowser.Controller/Library/IUserManager.cs | 9 ++++++--- 4 files changed, 26 insertions(+), 29 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index 05efe2355..4a67c1aed 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -109,7 +109,7 @@ namespace Jellyfin.Api.Controllers var userDataPath = Path.Combine(_serverConfigurationManager.ApplicationPaths.UserConfigurationDirectoryPath, user.Username); if (user.ProfileImage != null) { - _userManager.ClearProfileImage(user); + await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false); } user.ProfileImage = new Data.Entities.ImageInfo(Path.Combine(userDataPath, "profile" + MimeTypes.ToExtension(mimeType))); @@ -138,7 +138,7 @@ namespace Jellyfin.Api.Controllers [SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult DeleteUserImage( + public async Task DeleteUserImage( [FromRoute, Required] Guid userId, [FromRoute, Required] ImageType imageType, [FromRoute] int? index = null) @@ -158,7 +158,7 @@ namespace Jellyfin.Api.Controllers _logger.LogError(e, "Error deleting user profile image:"); } - _userManager.ClearProfileImage(user); + await _userManager.ClearProfileImageAsync(user).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 50bb8bb2a..7b0897bfb 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -381,17 +381,13 @@ namespace Jellyfin.Api.Controllers var user = _userManager.GetUserById(userId); - if (string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal)) - { - await _userManager.UpdateUserAsync(user).ConfigureAwait(false); - _userManager.UpdateConfiguration(user.Id, updateUser.Configuration); - } - else + if (!string.Equals(user.Username, updateUser.Name, StringComparison.Ordinal)) { await _userManager.RenameUser(user, updateUser.Name).ConfigureAwait(false); - _userManager.UpdateConfiguration(updateUser.Id, updateUser.Configuration); } + await _userManager.UpdateConfigurationAsync(user.Id, updateUser.Configuration).ConfigureAwait(false); + return NoContent(); } @@ -409,7 +405,7 @@ namespace Jellyfin.Api.Controllers [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult UpdateUserPolicy( + public async Task UpdateUserPolicy( [FromRoute, Required] Guid userId, [FromBody] UserPolicy newPolicy) { @@ -447,7 +443,7 @@ namespace Jellyfin.Api.Controllers _sessionManager.RevokeUserTokens(user.Id, currentToken); } - _userManager.UpdatePolicy(userId, newPolicy); + await _userManager.UpdatePolicyAsync(userId, newPolicy).ConfigureAwait(false); return NoContent(); } @@ -464,7 +460,7 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.DefaultAuthorization)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status403Forbidden)] - public ActionResult UpdateUserConfiguration( + public async Task UpdateUserConfiguration( [FromRoute, Required] Guid userId, [FromBody] UserConfiguration userConfig) { @@ -473,7 +469,7 @@ namespace Jellyfin.Api.Controllers return Forbid("User configuration update not allowed"); } - _userManager.UpdateConfiguration(userId, userConfig); + await _userManager.UpdateConfigurationAsync(userId, userConfig).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index b41a5ee5c..40b89ed28 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -434,9 +434,7 @@ namespace Jellyfin.Server.Implementations.Users if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy && user != null) { - UpdatePolicy(user.Id, hasNewUserPolicy.GetNewUserPolicy()); - - await UpdateUserAsync(user).ConfigureAwait(false); + await UpdatePolicyAsync(user.Id, hasNewUserPolicy.GetNewUserPolicy()).ConfigureAwait(false); } } } @@ -615,9 +613,9 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void UpdateConfiguration(Guid userId, UserConfiguration config) + public async Task UpdateConfigurationAsync(Guid userId, UserConfiguration config) { - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); var user = dbContext.Users .Include(u => u.Permissions) .Include(u => u.Preferences) @@ -644,13 +642,13 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); dbContext.Update(user); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); } /// - public void UpdatePolicy(Guid userId, UserPolicy policy) + public async Task UpdatePolicyAsync(Guid userId, UserPolicy policy) { - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); var user = dbContext.Users .Include(u => u.Permissions) .Include(u => u.Preferences) @@ -715,15 +713,15 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); dbContext.Update(user); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); } /// - public void ClearProfileImage(User user) + public async Task ClearProfileImageAsync(User user) { - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); dbContext.Remove(user.ProfileImage); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); user.ProfileImage = null; } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 6a4f5cf67..8fd3b8c34 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -158,7 +158,8 @@ namespace MediaBrowser.Controller.Library /// /// The user's Id. /// The request containing the new user configuration. - void UpdateConfiguration(Guid userId, UserConfiguration config); + /// A task representing the update. + Task UpdateConfigurationAsync(Guid userId, UserConfiguration config); /// /// This method updates the user's policy. @@ -167,12 +168,14 @@ namespace MediaBrowser.Controller.Library /// /// The user's Id. /// The request containing the new user policy. - void UpdatePolicy(Guid userId, UserPolicy policy); + /// A task representing the update. + Task UpdatePolicyAsync(Guid userId, UserPolicy policy); /// /// Clears the user's profile image. /// /// The user. - void ClearProfileImage(User user); + /// A task representing the clearing of the profile image. + Task ClearProfileImageAsync(User user); } } -- cgit v1.2.3 From c70710de32f99eb6dc7b1f4d650924cc7d8a286b Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 15 Nov 2020 09:30:04 -0700 Subject: Update user cache after updating user. --- Jellyfin.Server.Implementations/Users/UserManager.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 40b89ed28..f684d151d 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -158,7 +158,6 @@ namespace Jellyfin.Server.Implementations.Users user.Username = newName; await UpdateUserAsync(user).ConfigureAwait(false); - OnUserUpdated?.Invoke(this, new GenericEventArgs(user)); } @@ -167,6 +166,7 @@ namespace Jellyfin.Server.Implementations.Users { using var dbContext = _dbProvider.CreateContext(); dbContext.Users.Update(user); + _users[user.Id] = user; dbContext.SaveChanges(); } @@ -175,7 +175,7 @@ namespace Jellyfin.Server.Implementations.Users { await using var dbContext = _dbProvider.CreateContext(); dbContext.Users.Update(user); - + _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } @@ -642,6 +642,7 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes); dbContext.Update(user); + _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } @@ -713,6 +714,7 @@ namespace Jellyfin.Server.Implementations.Users user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); dbContext.Update(user); + _users[user.Id] = user; await dbContext.SaveChangesAsync().ConfigureAwait(false); } @@ -723,6 +725,7 @@ namespace Jellyfin.Server.Implementations.Users dbContext.Remove(user.ProfileImage); await dbContext.SaveChangesAsync().ConfigureAwait(false); user.ProfileImage = null; + _users[user.Id] = user; } private static bool IsValidUsername(string name) -- cgit v1.2.3 From 76f61eb0d6941782c3d3187e16a22333c8b13b4c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Fri, 11 Dec 2020 10:15:43 -0500 Subject: Convert DeleteUser to async --- Jellyfin.Api/Controllers/UserController.cs | 4 ++-- Jellyfin.Server.Implementations/Users/UserManager.cs | 8 ++++---- MediaBrowser.Controller/Library/IUserManager.cs | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Api/Controllers/UserController.cs b/Jellyfin.Api/Controllers/UserController.cs index 0f7c25d0e..d61417480 100644 --- a/Jellyfin.Api/Controllers/UserController.cs +++ b/Jellyfin.Api/Controllers/UserController.cs @@ -133,11 +133,11 @@ namespace Jellyfin.Api.Controllers [Authorize(Policy = Policies.RequiresElevation)] [ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status404NotFound)] - public ActionResult DeleteUser([FromRoute, Required] Guid userId) + public async Task DeleteUser([FromRoute, Required] Guid userId) { var user = _userManager.GetUserById(userId); _sessionManager.RevokeUserTokens(user.Id, null); - _userManager.DeleteUser(userId); + await _userManager.DeleteUserAsync(userId).ConfigureAwait(false); return NoContent(); } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index f684d151d..e37fcc908 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -220,7 +220,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void DeleteUser(Guid userId) + public async Task DeleteUserAsync(Guid userId) { if (!_users.TryGetValue(userId, out var user)) { @@ -246,7 +246,7 @@ namespace Jellyfin.Server.Implementations.Users nameof(userId)); } - using var dbContext = _dbProvider.CreateContext(); + await using var dbContext = _dbProvider.CreateContext(); // Clear all entities related to the user from the database. if (user.ProfileImage != null) @@ -258,10 +258,10 @@ namespace Jellyfin.Server.Implementations.Users dbContext.RemoveRange(user.Preferences); dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); - dbContext.SaveChanges(); + await dbContext.SaveChangesAsync().ConfigureAwait(false); _users.Remove(userId); - _eventManager.Publish(new UserDeletedEventArgs(user)); + await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false); } /// diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 8fd3b8c34..6e267834b 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -93,7 +93,8 @@ namespace MediaBrowser.Controller.Library /// Deletes the specified user. /// /// The id of the user to be deleted. - void DeleteUser(Guid userId); + /// A task representing the deletion of the user. + Task DeleteUserAsync(Guid userId); /// /// Resets the password. -- cgit v1.2.3 From b670937c3d373173159a40f803e6e907ec0cd060 Mon Sep 17 00:00:00 2001 From: crobibero Date: Fri, 11 Dec 2020 15:00:43 -0700 Subject: Use typed UserManager GetPreference --- .../Library/UserViewManager.cs | 12 ++++---- Emby.Server.Implementations/TV/TVSeriesManager.cs | 3 +- Jellyfin.Api/Controllers/ItemsController.cs | 16 +++++----- Jellyfin.Data/Entities/User.cs | 36 ++++++++++++++++++++-- .../Users/UserManager.cs | 18 +++++------ .../Migrations/Routines/MigrateUserDb.cs | 4 +-- MediaBrowser.Controller/Channels/Channel.cs | 8 ++--- .../Entities/Audio/MusicAlbum.cs | 2 +- .../Entities/Audio/MusicArtist.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 8 ++--- MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 2 +- MediaBrowser.Controller/Entities/TV/Series.cs | 2 +- 12 files changed, 71 insertions(+), 42 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index e4221dd50..6c74e6bd4 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -129,23 +129,23 @@ namespace Emby.Server.Implementations.Library if (!query.IncludeHidden) { - list = list.Where(i => !user.GetPreference(PreferenceKind.MyMediaExcludes).Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))).ToList(); + list = list.Where(i => !user.GetPreference(PreferenceKind.MyMediaExcludes).Contains(i.Id)).ToList(); } var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList(); - var orders = user.GetPreference(PreferenceKind.OrderedViews).ToList(); + var orders = user.GetPreference(PreferenceKind.OrderedViews); return list .OrderBy(i => { - var index = orders.IndexOf(i.Id.ToString("D", CultureInfo.InvariantCulture)); + var index = Array.IndexOf(orders, i.Id); if (index == -1 && i is UserView view && view.DisplayParentId != Guid.Empty) { - index = orders.IndexOf(view.DisplayParentId.ToString("D", CultureInfo.InvariantCulture)); + index = Array.IndexOf(orders, view.DisplayParentId); } return index == -1 ? int.MaxValue : index; @@ -280,8 +280,8 @@ namespace Emby.Server.Implementations.Library { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes) - .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) + .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes) + .Contains(i.Id)) .ToList(); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 447c587f9..a2a9b37a7 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -75,8 +75,7 @@ namespace Emby.Server.Implementations.TV { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes) - .Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) + .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes).Contains(i.Id)) .ToArray(); } diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 7e9035f80..0816efb38 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -254,18 +254,18 @@ namespace Jellyfin.Api.Controllers includeItemTypes = new[] { "Playlist" }; } - bool isInEnabledFolder = user!.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id) + var enabledChannels = user!.GetPreference(PreferenceKind.EnabledChannels); + + bool isInEnabledFolder = Array.IndexOf(user.GetPreference(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled - || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id) + || Array.IndexOf(enabledChannels, item.Id) != -1 // Assume all items inside an EnabledChannel are enabled - || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.ChannelId); + || Array.IndexOf(enabledChannels, item.ChannelId) != -1; var collectionFolders = _libraryManager.GetCollectionFolders(item); foreach (var collectionFolder in collectionFolders) { - if (user.GetPreference(PreferenceKind.EnabledFolders).Contains( - collectionFolder.Id.ToString("N", CultureInfo.InvariantCulture), - StringComparer.OrdinalIgnoreCase)) + if (user.GetPreference(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) { isInEnabledFolder = true; } @@ -786,12 +786,12 @@ namespace Jellyfin.Api.Controllers var ancestorIds = Array.Empty(); - var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes); + var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes); if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0) { ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !excludeFolderIds.Contains(i.Id.ToString("N", CultureInfo.InvariantCulture))) + .Where(i => !excludeFolderIds.Contains(i.Id)) .Select(i => i.Id) .ToArray(); } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 6d4681914..0733cc670 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Globalization; @@ -413,6 +414,25 @@ namespace Jellyfin.Data.Entities return Equals(val, string.Empty) ? Array.Empty() : val.Split(Delimiter); } + /// + /// Gets the user's preferences for the given preference kind. + /// + /// The preference kind. + /// Type of preference. + /// A {T} array containing the user's preference. + public T[] GetPreference(PreferenceKind preference) + { + var val = Preferences.First(p => p.Kind == preference).Value; + if (string.IsNullOrEmpty(val)) + { + return Array.Empty(); + } + + var converter = TypeDescriptor.GetConverter(typeof(T)); + var stringValues = val.Split(Delimiter); + return Array.ConvertAll(stringValues, value => (T)converter.ConvertFromString(value)); + } + /// /// Sets the specified preference to the given value. /// @@ -421,7 +441,19 @@ namespace Jellyfin.Data.Entities public void SetPreference(PreferenceKind preference, string[] values) { Preferences.First(p => p.Kind == preference).Value - = string.Join(Delimiter.ToString(CultureInfo.InvariantCulture), values); + = string.Join(Delimiter, values); + } + + /// + /// Sets the specified preference to the given value. + /// + /// The preference kind. + /// The values. + /// The type of value. + public void SetPreference(PreferenceKind preference, T[] values) + { + Preferences.First(p => p.Kind == preference).Value + = string.Join(Delimiter, values); } /// @@ -441,7 +473,7 @@ namespace Jellyfin.Data.Entities /// True if the folder is in the user's grouped folders. public bool IsFolderGrouped(Guid id) { - return GetPreference(PreferenceKind.GroupedFolders).Any(i => new Guid(i) == id); + return Array.IndexOf(GetPreference(PreferenceKind.GroupedFolders), id) != -1; } private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index e37fcc908..f576e662a 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -376,14 +376,14 @@ namespace Jellyfin.Server.Implementations.Users EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing), AccessSchedules = user.AccessSchedules.ToArray(), BlockedTags = user.GetPreference(PreferenceKind.BlockedTags), - EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels)?.Select(Guid.Parse).ToArray(), + EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels), EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices), - EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders)?.Select(Guid.Parse).ToArray(), + EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders), EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders), SyncPlayAccess = user.SyncPlayAccess, - BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels)?.Select(Guid.Parse).ToArray(), - BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders)?.Select(Guid.Parse).ToArray(), - BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems).Select(Enum.Parse).ToArray() + BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels), + BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders), + BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems) } }; } @@ -704,13 +704,11 @@ namespace Jellyfin.Server.Implementations.Users } // TODO: fix this at some point - user.SetPreference( - PreferenceKind.BlockUnratedItems, - policy.BlockUnratedItems?.Select(i => i.ToString()).ToArray() ?? Array.Empty()); + user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty()); user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); - user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); + user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); - user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); + user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); dbContext.Update(user); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 74c550331..33f039c39 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -168,9 +168,9 @@ namespace Jellyfin.Server.Migrations.Routines } user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags); - user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); + user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels); user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices); - user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders?.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray()); + user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders); user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders); user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews); user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders); diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index 129cdb519..c8a8db528 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -17,9 +17,10 @@ namespace MediaBrowser.Controller.Channels { public override bool IsVisible(User user) { - if (user.GetPreference(PreferenceKind.BlockedChannels) != null) + var blockedChannelsPreference = user.GetPreference(PreferenceKind.BlockedChannels); + if (blockedChannelsPreference.Length != 0) { - if (user.GetPreference(PreferenceKind.BlockedChannels).Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) + if (blockedChannelsPreference.Contains(Id)) { return false; } @@ -27,8 +28,7 @@ namespace MediaBrowser.Controller.Channels else { if (!user.HasPermission(PermissionKind.EnableAllChannels) - && !user.GetPreference(PreferenceKind.EnabledChannels) - .Contains(Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) + && !user.GetPreference(PreferenceKind.EnabledChannels).Contains(Id)) { return false; } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 48cd9371a..260daa1c2 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Entities.Audio protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music.ToString()); + return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music); } public override UnratedItem GetBlockUnratedType() diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index c5e50cf45..5d586bfb5 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -145,7 +145,7 @@ namespace MediaBrowser.Controller.Entities.Audio protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music.ToString()); + return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music); } public override UnratedItem GetBlockUnratedType() diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 1b25fbdbb..3aa93565b 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -480,11 +480,11 @@ namespace MediaBrowser.Controller.Entities return true; } - var allowed = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders); + var allowed = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders); if (SourceType == SourceType.Channel) { - return allowed.Contains(ChannelId.ToString(""), StringComparer.OrdinalIgnoreCase); + return allowed.Contains(ChannelId); } else { @@ -492,7 +492,7 @@ namespace MediaBrowser.Controller.Entities foreach (var folder in collectionFolders) { - if (allowed.Contains(folder.Id.ToString("N", CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase)) + if (allowed.Contains(folder.Id)) { return true; } @@ -1909,7 +1909,7 @@ namespace MediaBrowser.Controller.Entities return false; } - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(GetBlockUnratedType().ToString()); + return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(GetBlockUnratedType()); } /// diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 8de88cc1b..343da3825 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Controller.Entities.Movies protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie.ToString()); + return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie); } public override double GetDefaultPrimaryImageAspectRatio() diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index e8afa9a49..aaed879c3 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -452,7 +452,7 @@ namespace MediaBrowser.Controller.Entities.TV protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series.ToString()); + return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series); } public override UnratedItem GetBlockUnratedType() -- cgit v1.2.3 From d9263dacd5d2e3594e08a5c75bedb31cfc4c5c97 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 12 Dec 2020 11:20:48 -0500 Subject: Enable nullable for Jellyfin.Server.Implementations --- Jellyfin.Server.Implementations/Activity/ActivityManager.cs | 2 +- .../Events/Consumers/Session/PlaybackStartLogger.cs | 2 +- .../Events/Consumers/Session/PlaybackStopLogger.cs | 2 +- .../Jellyfin.Server.Implementations.csproj | 1 + Jellyfin.Server.Implementations/JellyfinDb.cs | 1 + .../Users/DefaultAuthenticationProvider.cs | 2 -- Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs | 2 -- Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs | 3 +-- Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs | 2 -- Jellyfin.Server.Implementations/Users/UserManager.cs | 3 +-- .../ValueConverters/DateTimeKindValueConverter.cs | 4 ++-- 11 files changed, 9 insertions(+), 15 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs index 7bde4f35b..27360afb0 100644 --- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs +++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs @@ -27,7 +27,7 @@ namespace Jellyfin.Server.Implementations.Activity } /// - public event EventHandler> EntryCreated; + public event EventHandler>? EntryCreated; /// public async Task CreateAsync(ActivityLog entry) diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs index ec4a76e7f..0340248bb 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs @@ -86,7 +86,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session return name; } - private static string GetPlaybackNotificationType(string mediaType) + private static string? GetPlaybackNotificationType(string mediaType) { if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs index a0bad29e9..1648b1b47 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStopLogger.cs @@ -94,7 +94,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session return name; } - private static string GetPlaybackStoppedNotificationType(string mediaType) + private static string? GetPlaybackStoppedNotificationType(string mediaType) { if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 5f508ea0a..9e4a2065f 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -5,6 +5,7 @@ false true true + enable diff --git a/Jellyfin.Server.Implementations/JellyfinDb.cs b/Jellyfin.Server.Implementations/JellyfinDb.cs index 7f3f83749..39f842354 100644 --- a/Jellyfin.Server.Implementations/JellyfinDb.cs +++ b/Jellyfin.Server.Implementations/JellyfinDb.cs @@ -1,3 +1,4 @@ +#nullable disable #pragma warning disable CS1591 using System; diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs index 662b4bf65..6a78e7ee6 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Linq; using System.Text; diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs index 334f27f85..9cc1c3e5e 100644 --- a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs +++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs @@ -1,5 +1,3 @@ -#nullable enable - using System; using System.Collections.Generic; using System.IO; diff --git a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs index 1fb89c4a6..dbba80c21 100644 --- a/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs +++ b/Jellyfin.Server.Implementations/Users/DeviceAccessEntryPoint.cs @@ -1,5 +1,4 @@ -#nullable enable -#pragma warning disable CS1591 +#pragma warning disable CS1591 using System.Threading.Tasks; using Jellyfin.Data.Entities; diff --git a/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs index 5f32479e1..c4e4c460a 100644 --- a/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs +++ b/Jellyfin.Server.Implementations/Users/InvalidAuthProvider.cs @@ -1,5 +1,3 @@ -#nullable enable - using System.Threading.Tasks; using Jellyfin.Data.Entities; using MediaBrowser.Controller.Authentication; diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index e37fcc908..b76b272cf 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -1,5 +1,4 @@ -#nullable enable -#pragma warning disable CA1307 +#pragma warning disable CA1307 using System; using System.Collections.Concurrent; diff --git a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs index 8a510898b..a9a18c823 100644 --- a/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs +++ b/Jellyfin.Server.Implementations/ValueConverters/DateTimeKindValueConverter.cs @@ -13,9 +13,9 @@ namespace Jellyfin.Server.Implementations.ValueConverters /// /// The kind to specify. /// The mapping hints. - public DateTimeKindValueConverter(DateTimeKind kind, ConverterMappingHints mappingHints = null) + public DateTimeKindValueConverter(DateTimeKind kind, ConverterMappingHints? mappingHints = null) : base(v => v.ToUniversalTime(), v => DateTime.SpecifyKind(v, kind), mappingHints) { } } -} \ No newline at end of file +} -- cgit v1.2.3 From ee23d06154133ffc506dda3a8ef0e09d20c03c6c Mon Sep 17 00:00:00 2001 From: crobibero Date: Sun, 13 Dec 2020 08:15:26 -0700 Subject: Use a more descriptive function name --- Emby.Server.Implementations/Library/UserViewManager.cs | 6 +++--- Emby.Server.Implementations/TV/TVSeriesManager.cs | 2 +- Jellyfin.Api/Controllers/ItemsController.cs | 8 ++++---- Jellyfin.Data/Entities/User.cs | 4 ++-- Jellyfin.Server.Implementations/Users/UserManager.cs | 10 +++++----- MediaBrowser.Controller/Channels/Channel.cs | 4 ++-- MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs | 2 +- MediaBrowser.Controller/Entities/Audio/MusicArtist.cs | 2 +- MediaBrowser.Controller/Entities/BaseItem.cs | 4 ++-- MediaBrowser.Controller/Entities/Folder.cs | 4 ++-- MediaBrowser.Controller/Entities/Movies/BoxSet.cs | 2 +- MediaBrowser.Controller/Entities/TV/Series.cs | 2 +- 12 files changed, 25 insertions(+), 25 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index 6c74e6bd4..b6b7ea949 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -129,12 +129,12 @@ namespace Emby.Server.Implementations.Library if (!query.IncludeHidden) { - list = list.Where(i => !user.GetPreference(PreferenceKind.MyMediaExcludes).Contains(i.Id)).ToList(); + list = list.Where(i => !user.GetPreferenceValues(PreferenceKind.MyMediaExcludes).Contains(i.Id)).ToList(); } var sorted = _libraryManager.Sort(list, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending).ToList(); - var orders = user.GetPreference(PreferenceKind.OrderedViews); + var orders = user.GetPreferenceValues(PreferenceKind.OrderedViews); return list .OrderBy(i => @@ -280,7 +280,7 @@ namespace Emby.Server.Implementations.Library { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes) + .Where(i => !user.GetPreferenceValues(PreferenceKind.LatestItemExcludes) .Contains(i.Id)) .ToList(); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index a2a9b37a7..f0734340b 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -75,7 +75,7 @@ namespace Emby.Server.Implementations.TV { parents = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) - .Where(i => !user.GetPreference(PreferenceKind.LatestItemExcludes).Contains(i.Id)) + .Where(i => !user.GetPreferenceValues(PreferenceKind.LatestItemExcludes).Contains(i.Id)) .ToArray(); } diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs index 0816efb38..b84136ac6 100644 --- a/Jellyfin.Api/Controllers/ItemsController.cs +++ b/Jellyfin.Api/Controllers/ItemsController.cs @@ -254,9 +254,9 @@ namespace Jellyfin.Api.Controllers includeItemTypes = new[] { "Playlist" }; } - var enabledChannels = user!.GetPreference(PreferenceKind.EnabledChannels); + var enabledChannels = user!.GetPreferenceValues(PreferenceKind.EnabledChannels); - bool isInEnabledFolder = Array.IndexOf(user.GetPreference(PreferenceKind.EnabledFolders), item.Id) != -1 + bool isInEnabledFolder = Array.IndexOf(user.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.Id) != -1 // Assume all items inside an EnabledChannel are enabled @@ -265,7 +265,7 @@ namespace Jellyfin.Api.Controllers var collectionFolders = _libraryManager.GetCollectionFolders(item); foreach (var collectionFolder in collectionFolders) { - if (user.GetPreference(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) + if (user.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) { isInEnabledFolder = true; } @@ -786,7 +786,7 @@ namespace Jellyfin.Api.Controllers var ancestorIds = Array.Empty(); - var excludeFolderIds = user.GetPreference(PreferenceKind.LatestItemExcludes); + var excludeFolderIds = user.GetPreferenceValues(PreferenceKind.LatestItemExcludes); if (parentIdGuid.Equals(Guid.Empty) && excludeFolderIds.Length > 0) { ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index d19cbac7f..cc85a0b85 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -419,7 +419,7 @@ namespace Jellyfin.Data.Entities /// The preference kind. /// Type of preference. /// A {T} array containing the user's preference. - public T[] GetPreference(PreferenceKind preference) + public T[] GetPreferenceValues(PreferenceKind preference) { var val = Preferences.First(p => p.Kind == preference).Value; if (string.IsNullOrEmpty(val)) @@ -499,7 +499,7 @@ namespace Jellyfin.Data.Entities /// True if the folder is in the user's grouped folders. public bool IsFolderGrouped(Guid id) { - return Array.IndexOf(GetPreference(PreferenceKind.GroupedFolders), id) != -1; + return Array.IndexOf(GetPreferenceValues(PreferenceKind.GroupedFolders), id) != -1; } private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date) diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index f576e662a..400f02ef2 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -376,14 +376,14 @@ namespace Jellyfin.Server.Implementations.Users EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing), AccessSchedules = user.AccessSchedules.ToArray(), BlockedTags = user.GetPreference(PreferenceKind.BlockedTags), - EnabledChannels = user.GetPreference(PreferenceKind.EnabledChannels), + EnabledChannels = user.GetPreferenceValues(PreferenceKind.EnabledChannels), EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices), - EnabledFolders = user.GetPreference(PreferenceKind.EnabledFolders), + EnabledFolders = user.GetPreferenceValues(PreferenceKind.EnabledFolders), EnableContentDeletionFromFolders = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders), SyncPlayAccess = user.SyncPlayAccess, - BlockedChannels = user.GetPreference(PreferenceKind.BlockedChannels), - BlockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders), - BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems) + BlockedChannels = user.GetPreferenceValues(PreferenceKind.BlockedChannels), + BlockedMediaFolders = user.GetPreferenceValues(PreferenceKind.BlockedMediaFolders), + BlockUnratedItems = user.GetPreferenceValues(PreferenceKind.BlockUnratedItems) } }; } diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index c8a8db528..b2315bda4 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -17,7 +17,7 @@ namespace MediaBrowser.Controller.Channels { public override bool IsVisible(User user) { - var blockedChannelsPreference = user.GetPreference(PreferenceKind.BlockedChannels); + var blockedChannelsPreference = user.GetPreferenceValues(PreferenceKind.BlockedChannels); if (blockedChannelsPreference.Length != 0) { if (blockedChannelsPreference.Contains(Id)) @@ -28,7 +28,7 @@ namespace MediaBrowser.Controller.Channels else { if (!user.HasPermission(PermissionKind.EnableAllChannels) - && !user.GetPreference(PreferenceKind.EnabledChannels).Contains(Id)) + && !user.GetPreferenceValues(PreferenceKind.EnabledChannels).Contains(Id)) { return false; } diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 260daa1c2..9a33ad9d7 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -120,7 +120,7 @@ namespace MediaBrowser.Controller.Entities.Audio protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music); + return user.GetPreferenceValues(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music); } public override UnratedItem GetBlockUnratedType() diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 5d586bfb5..8a9bb12c7 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -145,7 +145,7 @@ namespace MediaBrowser.Controller.Entities.Audio protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music); + return user.GetPreferenceValues(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Music); } public override UnratedItem GetBlockUnratedType() diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3aa93565b..cbb02aabd 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -480,7 +480,7 @@ namespace MediaBrowser.Controller.Entities return true; } - var allowed = user.GetPreference(PreferenceKind.EnableContentDeletionFromFolders); + var allowed = user.GetPreferenceValues(PreferenceKind.EnableContentDeletionFromFolders); if (SourceType == SourceType.Channel) { @@ -1909,7 +1909,7 @@ namespace MediaBrowser.Controller.Entities return false; } - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(GetBlockUnratedType()); + return user.GetPreferenceValues(PreferenceKind.BlockUnratedItems).Contains(GetBlockUnratedType()); } /// diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 0f936538b..cac5026f7 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -186,7 +186,7 @@ namespace MediaBrowser.Controller.Entities { if (this is ICollectionFolder && !(this is BasePluginFolder)) { - var blockedMediaFolders = user.GetPreference(PreferenceKind.BlockedMediaFolders); + var blockedMediaFolders = user.GetPreferenceValues(PreferenceKind.BlockedMediaFolders); if (blockedMediaFolders.Length > 0) { if (blockedMediaFolders.Contains(Id)) @@ -197,7 +197,7 @@ namespace MediaBrowser.Controller.Entities else { if (!user.HasPermission(PermissionKind.EnableAllFolders) - && !user.GetPreference(PreferenceKind.EnabledFolders).Contains(Id)) + && !user.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(Id)) { return false; } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 343da3825..05e4229ca 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -49,7 +49,7 @@ namespace MediaBrowser.Controller.Entities.Movies protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie); + return user.GetPreferenceValues(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie); } public override double GetDefaultPrimaryImageAspectRatio() diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index aaed879c3..1a379074d 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -452,7 +452,7 @@ namespace MediaBrowser.Controller.Entities.TV protected override bool GetBlockUnratedValue(User user) { - return user.GetPreference(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series); + return user.GetPreferenceValues(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Series); } public override UnratedItem GetBlockUnratedType() -- cgit v1.2.3 From a5e55ba85956fe54539cb22dfd95c9499aa3de6c Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Feb 2021 15:59:27 -0500 Subject: Clean up UserManager.AuthenticateUser --- .../Users/UserManager.cs | 23 +++++++--------------- 1 file changed, 7 insertions(+), 16 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d1de5408c..38a074e98 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -402,27 +402,18 @@ namespace Jellyfin.Server.Implementations.Users } var user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase)); - bool success; - IAuthenticationProvider? authenticationProvider; + var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint) + .ConfigureAwait(false); + var authenticationProvider = authResult.authenticationProvider; + var success = authResult.success; - if (user != null) + if (user is null) { - var authResult = await AuthenticateLocalUser(username, password, user, remoteEndPoint) - .ConfigureAwait(false); - authenticationProvider = authResult.authenticationProvider; - success = authResult.success; - } - else - { - var authResult = await AuthenticateLocalUser(username, password, null, remoteEndPoint) - .ConfigureAwait(false); - authenticationProvider = authResult.authenticationProvider; string updatedUsername = authResult.username; - success = authResult.success; if (success - && authenticationProvider != null - && !(authenticationProvider is DefaultAuthenticationProvider)) + && authenticationProvider is not null + && authenticationProvider is not DefaultAuthenticationProvider) { // Trust the username returned by the authentication provider username = updatedUsername; -- cgit v1.2.3 From 1171b5ab923e0a0ad6b412201ed7ba0d33f51229 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 16 Feb 2021 20:41:53 -0500 Subject: Fix user renaming logic --- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index d1de5408c..95736c37f 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -147,7 +147,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("The new and old names must be different."); } - if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.Ordinal))) + if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase))) { throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, -- cgit v1.2.3 From f127096660a0eed7da2ef6db68720eba4dfbd7a0 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Tue, 16 Feb 2021 20:48:41 -0500 Subject: Don't allow new users to be created with the same name as an existing user. --- Jellyfin.Server.Implementations/Users/UserManager.cs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 95736c37f..a3a9e90d4 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -206,6 +206,14 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)"); } + if (Users.Any(u => u.Username.Equals(name, StringComparison.OrdinalIgnoreCase))) + { + throw new ArgumentException(string.Format( + CultureInfo.InvariantCulture, + "A user with the name '{0}' already exists.", + name)); + } + await using var dbContext = _dbProvider.CreateContext(); var newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false); -- cgit v1.2.3 From 442e7706880bba9a95404b4d04972674ad65d085 Mon Sep 17 00:00:00 2001 From: cvium Date: Wed, 17 Feb 2021 11:30:14 +0100 Subject: Validate the new username when renaming --- .../Properties/AssemblyInfo.cs | 23 ++++++++++++++++++ .../Users/UserManager.cs | 22 ++++++++++------- .../Jellyfin.Server.Implementations.Tests.csproj | 1 + .../Users/UserManagerTests.cs | 28 ++++++++++++++++++++++ 4 files changed, 65 insertions(+), 9 deletions(-) create mode 100644 Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs create mode 100644 tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs b/Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..6528d4fdc --- /dev/null +++ b/Jellyfin.Server.Implementations/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Jellyfin.Server.Implementations")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Jellyfin Project")] +[assembly: AssemblyProduct("Jellyfin Server")] +[assembly: AssemblyCopyright("Copyright © 2019 Jellyfin Contributors. Code released under the GNU General Public License")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en")] +[assembly: InternalsVisibleTo("Jellyfin.Server.Implementations.Tests")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index a3a9e90d4..76d1389ca 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -137,10 +137,7 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentNullException(nameof(user)); } - if (string.IsNullOrWhiteSpace(newName)) - { - throw new ArgumentException("Invalid username", nameof(newName)); - } + ThrowIfInvalidUsername(newName); if (user.Username.Equals(newName, StringComparison.Ordinal)) { @@ -201,10 +198,7 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task CreateUserAsync(string name) { - if (!IsValidUsername(name)) - { - throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)"); - } + ThrowIfInvalidUsername(name); if (Users.Any(u => u.Username.Equals(name, StringComparison.OrdinalIgnoreCase))) { @@ -733,12 +727,22 @@ namespace Jellyfin.Server.Implementations.Users _users[user.Id] = user; } + internal static void ThrowIfInvalidUsername(string name) + { + if (!string.IsNullOrWhiteSpace(name) && IsValidUsername(name)) + { + return; + } + + throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)", nameof(name)); + } + private static bool IsValidUsername(string name) { // This is some regex that matches only on unicode "word" characters, as well as -, _ and @ // In theory this will cut out most if not all 'control' characters which should help minimize any weirdness // Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), periods (.) and spaces ( ) - return Regex.IsMatch(name, @"^[\w\ \-'._@]*$"); + return Regex.IsMatch(name, @"^[\w\ \-'._@]+$"); } private IAuthenticationProvider GetAuthenticationProvider(User user) diff --git a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj index 174f29b09..c3b3155fe 100644 --- a/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj +++ b/tests/Jellyfin.Server.Implementations.Tests/Jellyfin.Server.Implementations.Tests.csproj @@ -39,6 +39,7 @@ + diff --git a/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs new file mode 100644 index 000000000..9bcd43bc0 --- /dev/null +++ b/tests/Jellyfin.Server.Implementations.Tests/Users/UserManagerTests.cs @@ -0,0 +1,28 @@ +using System; +using Jellyfin.Server.Implementations.Users; +using Xunit; + +namespace Jellyfin.Server.Implementations.Tests.Users +{ + public class UserManagerTests + { + [Theory] + [InlineData("this_is_valid", true)] + [InlineData("this is also valid", true)] + [InlineData(" ", false)] + [InlineData("", false)] + [InlineData("0@_-' .", true)] + public void ThrowIfInvalidUsername_WhenInvalidUsername_ThrowsArgumentException(string username, bool isValid) + { + var ex = Record.Exception(() => UserManager.ThrowIfInvalidUsername(username)); + + var argumentExceptionNotThrown = ex is not ArgumentException; + if (ex != null) + { + Assert.Equal(typeof(ArgumentException), ex.GetType()); + } + + Assert.Equal(isValid, argumentExceptionNotThrown); + } + } +} -- cgit v1.2.3 From 03cc6b1d782067e58975dbea4b61e42a3c04e1aa Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 25 Feb 2021 19:02:27 -0500 Subject: Make styling more consistent --- Jellyfin.Server.Implementations/Users/UserManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 38a074e98..25d7524c7 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -407,12 +407,12 @@ namespace Jellyfin.Server.Implementations.Users var authenticationProvider = authResult.authenticationProvider; var success = authResult.success; - if (user is null) + if (user == null) { string updatedUsername = authResult.username; if (success - && authenticationProvider is not null + && authenticationProvider != null && authenticationProvider is not DefaultAuthenticationProvider) { // Trust the username returned by the authentication provider -- cgit v1.2.3 From f638ee6b0918c2ef05ec11cfa43584d3efad68d1 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 6 Mar 2021 17:43:01 -0500 Subject: Enable nullable for Jellyfin.Data and remove unnecessary attributes --- Emby.Drawing/ImageProcessor.cs | 7 ++++++- Jellyfin.Api/Controllers/ImageController.cs | 12 +++++++++++- Jellyfin.Api/Controllers/StartupController.cs | 5 ++++- Jellyfin.Data/Entities/AccessSchedule.cs | 7 ------- Jellyfin.Data/Entities/ActivityLog.cs | 8 +++----- Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs | 3 --- Jellyfin.Data/Entities/DisplayPreferences.cs | 7 ++----- Jellyfin.Data/Entities/Group.cs | 1 - Jellyfin.Data/Entities/HomeSection.cs | 1 - Jellyfin.Data/Entities/ImageInfo.cs | 1 - Jellyfin.Data/Entities/ItemDisplayPreferences.cs | 2 -- Jellyfin.Data/Entities/Libraries/Artwork.cs | 1 - Jellyfin.Data/Entities/Libraries/BookMetadata.cs | 2 -- Jellyfin.Data/Entities/Libraries/Chapter.cs | 3 +-- Jellyfin.Data/Entities/Libraries/Collection.cs | 2 +- Jellyfin.Data/Entities/Libraries/CollectionItem.cs | 13 +++++++++++-- Jellyfin.Data/Entities/Libraries/Company.cs | 2 +- Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs | 8 ++++---- Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs | 2 -- Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs | 6 +++--- Jellyfin.Data/Entities/Libraries/Genre.cs | 1 - Jellyfin.Data/Entities/Libraries/ItemMetadata.cs | 6 ++---- Jellyfin.Data/Entities/Libraries/Library.cs | 12 +++--------- Jellyfin.Data/Entities/Libraries/LibraryItem.cs | 1 - Jellyfin.Data/Entities/Libraries/MediaFile.cs | 1 - Jellyfin.Data/Entities/Libraries/MetadataProvider.cs | 1 - Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs | 5 +++-- Jellyfin.Data/Entities/Libraries/MovieMetadata.cs | 10 ++++------ Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs | 6 +++--- Jellyfin.Data/Entities/Libraries/Person.cs | 3 +-- Jellyfin.Data/Entities/Libraries/PersonRole.cs | 8 +++++--- Jellyfin.Data/Entities/Libraries/Rating.cs | 2 +- Jellyfin.Data/Entities/Libraries/RatingSource.cs | 4 ++-- Jellyfin.Data/Entities/Libraries/Release.cs | 1 - Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs | 2 +- Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs | 9 ++++----- Jellyfin.Data/Entities/Preference.cs | 1 - Jellyfin.Data/Entities/User.cs | 17 +++++++---------- Jellyfin.Data/Jellyfin.Data.csproj | 1 + .../Events/Consumers/Session/PlaybackStartLogger.cs | 4 ++-- Jellyfin.Server.Implementations/Users/UserManager.cs | 6 +++--- Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs | 4 +--- MediaBrowser.Controller/Drawing/IImageProcessor.cs | 2 +- tests/Jellyfin.Api.Tests/TestHelpers.cs | 4 ++-- 44 files changed, 93 insertions(+), 111 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index 8a2301d2d..aa8a3d212 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -352,8 +352,13 @@ namespace Emby.Drawing } /// - public string GetImageCacheTag(User user) + public string? GetImageCacheTag(User user) { + if (user.ProfileImage == null) + { + return null; + } + return (user.ProfileImage.Path + user.ProfileImage.LastModified.Ticks).GetMD5() .ToString("N", CultureInfo.InvariantCulture); } diff --git a/Jellyfin.Api/Controllers/ImageController.cs b/Jellyfin.Api/Controllers/ImageController.cs index a50d6e46b..b016f2450 100644 --- a/Jellyfin.Api/Controllers/ImageController.cs +++ b/Jellyfin.Api/Controllers/ImageController.cs @@ -196,6 +196,11 @@ namespace Jellyfin.Api.Controllers } var user = _userManager.GetUserById(userId); + if (user?.ProfileImage == null) + { + return NoContent(); + } + try { System.IO.File.Delete(user.ProfileImage.Path); @@ -235,6 +240,11 @@ namespace Jellyfin.Api.Controllers } var user = _userManager.GetUserById(userId); + if (user?.ProfileImage == null) + { + return NoContent(); + } + try { System.IO.File.Delete(user.ProfileImage.Path); @@ -1469,7 +1479,7 @@ namespace Jellyfin.Api.Controllers [FromQuery] int? imageIndex) { var user = _userManager.GetUserById(userId); - if (user == null) + if (user?.ProfileImage == null) { return NotFound(); } diff --git a/Jellyfin.Api/Controllers/StartupController.cs b/Jellyfin.Api/Controllers/StartupController.cs index d9cb34557..a01a617fc 100644 --- a/Jellyfin.Api/Controllers/StartupController.cs +++ b/Jellyfin.Api/Controllers/StartupController.cs @@ -132,7 +132,10 @@ namespace Jellyfin.Api.Controllers { var user = _userManager.Users.First(); - user.Username = startupUserDto.Name; + if (startupUserDto.Name != null) + { + user.Username = startupUserDto.Name; + } await _userManager.UpdateUserAsync(user).ConfigureAwait(false); diff --git a/Jellyfin.Data/Entities/AccessSchedule.cs b/Jellyfin.Data/Entities/AccessSchedule.cs index 72bca061d..9ac0666ac 100644 --- a/Jellyfin.Data/Entities/AccessSchedule.cs +++ b/Jellyfin.Data/Entities/AccessSchedule.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using System.Xml.Serialization; using Jellyfin.Data.Enums; @@ -33,8 +32,6 @@ namespace Jellyfin.Data.Entities /// Identity, Indexed, Required. /// [XmlIgnore] - [Key] - [Required] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } @@ -42,28 +39,24 @@ namespace Jellyfin.Data.Entities /// Gets or sets the id of the associated user. /// [XmlIgnore] - [Required] public Guid UserId { get; protected set; } /// /// Gets or sets the day of week. /// /// The day of week. - [Required] public DynamicDayOfWeek DayOfWeek { get; set; } /// /// Gets or sets the start hour. /// /// The start hour. - [Required] public double StartHour { get; set; } /// /// Gets or sets the end hour. /// /// The end hour. - [Required] public double EndHour { get; set; } /// diff --git a/Jellyfin.Data/Entities/ActivityLog.cs b/Jellyfin.Data/Entities/ActivityLog.cs index 80e32db30..e4534e8b5 100644 --- a/Jellyfin.Data/Entities/ActivityLog.cs +++ b/Jellyfin.Data/Entities/ActivityLog.cs @@ -50,7 +50,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 512. /// - [Required] [MaxLength(512)] [StringLength(512)] public string Name { get; set; } @@ -63,7 +62,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(512)] [StringLength(512)] - public string Overview { get; set; } + public string? Overview { get; set; } /// /// Gets or sets the short overview. @@ -73,7 +72,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(512)] [StringLength(512)] - public string ShortOverview { get; set; } + public string? ShortOverview { get; set; } /// /// Gets or sets the type. @@ -81,7 +80,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 256. /// - [Required] [MaxLength(256)] [StringLength(256)] public string Type { get; set; } @@ -102,7 +100,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(256)] [StringLength(256)] - public string ItemId { get; set; } + public string? ItemId { get; set; } /// /// Gets or sets the date created. This should be in UTC. diff --git a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs index d407180d4..cc46248c7 100644 --- a/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/CustomItemDisplayPreferences.cs @@ -57,7 +57,6 @@ namespace Jellyfin.Data.Entities /// /// Required. Max Length = 32. /// - [Required] [MaxLength(32)] [StringLength(32)] public string Client { get; set; } @@ -68,7 +67,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public string Key { get; set; } /// @@ -77,7 +75,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] public string Value { get; set; } } } diff --git a/Jellyfin.Data/Entities/DisplayPreferences.cs b/Jellyfin.Data/Entities/DisplayPreferences.cs index d186deb29..64cd6812a 100644 --- a/Jellyfin.Data/Entities/DisplayPreferences.cs +++ b/Jellyfin.Data/Entities/DisplayPreferences.cs @@ -30,8 +30,6 @@ namespace Jellyfin.Data.Entities SkipBackwardLength = 10000; ScrollDirection = ScrollDirection.Horizontal; ChromecastVersion = ChromecastVersion.Stable; - DashboardTheme = string.Empty; - TvHome = string.Empty; HomeSections = new HashSet(); } @@ -67,7 +65,6 @@ namespace Jellyfin.Data.Entities /// /// Required. Max Length = 32. /// - [Required] [MaxLength(32)] [StringLength(32)] public string Client { get; set; } @@ -138,14 +135,14 @@ namespace Jellyfin.Data.Entities /// [MaxLength(32)] [StringLength(32)] - public string DashboardTheme { get; set; } + public string? DashboardTheme { get; set; } /// /// Gets or sets the tv home screen. /// [MaxLength(32)] [StringLength(32)] - public string TvHome { get; set; } + public string? TvHome { get; set; } /// /// Gets or sets the home sections. diff --git a/Jellyfin.Data/Entities/Group.cs b/Jellyfin.Data/Entities/Group.cs index 8c45dde92..b14e22b7b 100644 --- a/Jellyfin.Data/Entities/Group.cs +++ b/Jellyfin.Data/Entities/Group.cs @@ -46,7 +46,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/HomeSection.cs b/Jellyfin.Data/Entities/HomeSection.cs index 062046260..5adc52491 100644 --- a/Jellyfin.Data/Entities/HomeSection.cs +++ b/Jellyfin.Data/Entities/HomeSection.cs @@ -15,7 +15,6 @@ namespace Jellyfin.Data.Entities /// /// Identity. Required. /// - [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; protected set; } diff --git a/Jellyfin.Data/Entities/ImageInfo.cs b/Jellyfin.Data/Entities/ImageInfo.cs index f9ae1a955..e0c37047d 100644 --- a/Jellyfin.Data/Entities/ImageInfo.cs +++ b/Jellyfin.Data/Entities/ImageInfo.cs @@ -39,7 +39,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] [MaxLength(512)] [StringLength(512)] public string Path { get; set; } diff --git a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs index f0a04f8ea..4bfeb2fa3 100644 --- a/Jellyfin.Data/Entities/ItemDisplayPreferences.cs +++ b/Jellyfin.Data/Entities/ItemDisplayPreferences.cs @@ -59,7 +59,6 @@ namespace Jellyfin.Data.Entities /// /// Required. Max Length = 32. /// - [Required] [MaxLength(32)] [StringLength(32)] public string Client { get; set; } @@ -99,7 +98,6 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] [MaxLength(64)] [StringLength(64)] public string SortBy { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/Artwork.cs b/Jellyfin.Data/Entities/Libraries/Artwork.cs index df28ce737..84a524de2 100644 --- a/Jellyfin.Data/Entities/Libraries/Artwork.cs +++ b/Jellyfin.Data/Entities/Libraries/Artwork.cs @@ -44,7 +44,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 65535. /// - [Required] [MaxLength(65535)] [StringLength(65535)] public string Path { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs index 8b0c96530..1ff4327b0 100644 --- a/Jellyfin.Data/Entities/Libraries/BookMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/BookMetadata.cs @@ -1,7 +1,6 @@ #pragma warning disable CA2227 using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; namespace Jellyfin.Data.Entities.Libraries @@ -32,7 +31,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection Publishers { get; protected set; } /// - [NotMapped] public ICollection Companies => Publishers; } } diff --git a/Jellyfin.Data/Entities/Libraries/Chapter.cs b/Jellyfin.Data/Entities/Libraries/Chapter.cs index f253143d7..11f53ae20 100644 --- a/Jellyfin.Data/Entities/Libraries/Chapter.cs +++ b/Jellyfin.Data/Entities/Libraries/Chapter.cs @@ -45,7 +45,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Name { get; set; } + public string? Name { get; set; } /// /// Gets or sets the language. @@ -54,7 +54,6 @@ namespace Jellyfin.Data.Entities.Libraries /// Required, Min length = 3, Max length = 3 /// ISO-639-3 3-character language codes. /// - [Required] [MinLength(3)] [MaxLength(3)] [StringLength(3)] diff --git a/Jellyfin.Data/Entities/Libraries/Collection.cs b/Jellyfin.Data/Entities/Libraries/Collection.cs index 39eded752..d230eeb2f 100644 --- a/Jellyfin.Data/Entities/Libraries/Collection.cs +++ b/Jellyfin.Data/Entities/Libraries/Collection.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Name { get; set; } + public string? Name { get; set; } /// [ConcurrencyCheck] diff --git a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs index 1157de442..e19362bdf 100644 --- a/Jellyfin.Data/Entities/Libraries/CollectionItem.cs +++ b/Jellyfin.Data/Entities/Libraries/CollectionItem.cs @@ -9,6 +9,15 @@ namespace Jellyfin.Data.Entities.Libraries /// public class CollectionItem : IHasConcurrencyToken { + /// + /// Initializes a new instance of the class. + /// + /// The library item. + public CollectionItem(LibraryItem libraryItem) + { + LibraryItem = libraryItem; + } + /// /// Gets or sets the id. /// @@ -36,7 +45,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// TODO check if this properly updated Dependant and has the proper principal relationship. /// - public virtual CollectionItem Next { get; set; } + public virtual CollectionItem? Next { get; set; } /// /// Gets or sets the previous item in the collection. @@ -44,7 +53,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// TODO check if this properly updated Dependant and has the proper principal relationship. /// - public virtual CollectionItem Previous { get; set; } + public virtual CollectionItem? Previous { get; set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Company.cs b/Jellyfin.Data/Entities/Libraries/Company.cs index 499ba3800..09050bb52 100644 --- a/Jellyfin.Data/Entities/Libraries/Company.cs +++ b/Jellyfin.Data/Entities/Libraries/Company.cs @@ -18,6 +18,7 @@ namespace Jellyfin.Data.Entities.Libraries public Company() { CompanyMetadata = new HashSet(); + ChildCompanies = new HashSet(); } /// @@ -44,7 +45,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection ChildCompanies { get; protected set; } /// - [NotMapped] public ICollection Companies => ChildCompanies; /// diff --git a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs index 86642b38a..a29f08c7f 100644 --- a/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CompanyMetadata.cs @@ -24,7 +24,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Description { get; set; } + public string? Description { get; set; } /// /// Gets or sets the headquarters. @@ -34,7 +34,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(255)] [StringLength(255)] - public string Headquarters { get; set; } + public string? Headquarters { get; set; } /// /// Gets or sets the country code. @@ -44,7 +44,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets the homepage. @@ -54,6 +54,6 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Homepage { get; set; } + public string? Homepage { get; set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs index a69a8eafa..af2393870 100644 --- a/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/CustomItemMetadata.cs @@ -1,5 +1,3 @@ -using System; - namespace Jellyfin.Data.Entities.Libraries { /// diff --git a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs index 5662decdb..b0ef11e0f 100644 --- a/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/EpisodeMetadata.cs @@ -24,7 +24,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } /// /// Gets or sets the plot. @@ -34,7 +34,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Plot { get; set; } + public string? Plot { get; set; } /// /// Gets or sets the tagline. @@ -44,6 +44,6 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Tagline { get; set; } + public string? Tagline { get; set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/Genre.cs b/Jellyfin.Data/Entities/Libraries/Genre.cs index befa75550..9f3d65028 100644 --- a/Jellyfin.Data/Entities/Libraries/Genre.cs +++ b/Jellyfin.Data/Entities/Libraries/Genre.cs @@ -33,7 +33,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Indexed, Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs index a0efa66e4..d12e011a8 100644 --- a/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/ItemMetadata.cs @@ -57,7 +57,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Title { get; set; } @@ -70,7 +69,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string OriginalTitle { get; set; } + public string? OriginalTitle { get; set; } /// /// Gets or sets the sort title. @@ -80,7 +79,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string SortTitle { get; set; } + public string? SortTitle { get; set; } /// /// Gets or sets the language. @@ -89,7 +88,6 @@ namespace Jellyfin.Data.Entities.Libraries /// Required, Min length = 3, Max length = 3. /// ISO-639-3 3-character language codes. /// - [Required] [MinLength(3)] [MaxLength(3)] [StringLength(3)] diff --git a/Jellyfin.Data/Entities/Libraries/Library.cs b/Jellyfin.Data/Entities/Libraries/Library.cs index 3ec4341a4..e45384902 100644 --- a/Jellyfin.Data/Entities/Libraries/Library.cs +++ b/Jellyfin.Data/Entities/Libraries/Library.cs @@ -1,4 +1,3 @@ -using System; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; @@ -14,14 +13,11 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The name of the library. - public Library(string name) + /// The path of the library. + public Library(string name, string path) { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException(nameof(name)); - } - Name = name; + Path = path; } /// @@ -39,7 +35,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 128. /// - [Required] [MaxLength(128)] [StringLength(128)] public string Name { get; set; } @@ -50,7 +45,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required. /// - [Required] public string Path { get; set; } /// diff --git a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs index 504b9c853..67ffad944 100644 --- a/Jellyfin.Data/Entities/Libraries/LibraryItem.cs +++ b/Jellyfin.Data/Entities/Libraries/LibraryItem.cs @@ -44,7 +44,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required. /// - [Required] public virtual Library Library { get; set; } /// diff --git a/Jellyfin.Data/Entities/Libraries/MediaFile.cs b/Jellyfin.Data/Entities/Libraries/MediaFile.cs index 7f64978e2..f3e2fe653 100644 --- a/Jellyfin.Data/Entities/Libraries/MediaFile.cs +++ b/Jellyfin.Data/Entities/Libraries/MediaFile.cs @@ -47,7 +47,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 65535. /// - [Required] [MaxLength(65535)] [StringLength(65535)] public string Path { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs index 20de5bf4b..fb2587882 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProvider.cs @@ -39,7 +39,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs index 12672dd25..2a9c904c8 100644 --- a/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs +++ b/Jellyfin.Data/Entities/Libraries/MetadataProviderId.cs @@ -14,7 +14,8 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The provider id. - public MetadataProviderId(string providerId) + /// The metadata provider. + public MetadataProviderId(string providerId, MetadataProvider metadataProvider) { if (string.IsNullOrEmpty(providerId)) { @@ -22,6 +23,7 @@ namespace Jellyfin.Data.Entities.Libraries } ProviderId = providerId; + MetadataProvider = metadataProvider; } /// @@ -39,7 +41,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string ProviderId { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs index 8cf7ca6a7..fb181dea6 100644 --- a/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MovieMetadata.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; using Jellyfin.Data.Interfaces; namespace Jellyfin.Data.Entities.Libraries @@ -30,7 +29,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } /// /// Gets or sets the tagline. @@ -40,7 +39,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Tagline { get; set; } + public string? Tagline { get; set; } /// /// Gets or sets the plot. @@ -50,7 +49,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Plot { get; set; } + public string? Plot { get; set; } /// /// Gets or sets the country code. @@ -60,7 +59,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets the studios that produced this movie. @@ -68,7 +67,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection Studios { get; protected set; } /// - [NotMapped] public ICollection Companies => Studios; } } diff --git a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs index 9e44e550a..3080bd692 100644 --- a/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/MusicAlbumMetadata.cs @@ -28,7 +28,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(255)] [StringLength(255)] - public string Barcode { get; set; } + public string? Barcode { get; set; } /// /// Gets or sets the label number. @@ -38,7 +38,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(255)] [StringLength(255)] - public string LabelNumber { get; set; } + public string? LabelNumber { get; set; } /// /// Gets or sets the country code. @@ -48,7 +48,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets a collection containing the labels. diff --git a/Jellyfin.Data/Entities/Libraries/Person.cs b/Jellyfin.Data/Entities/Libraries/Person.cs index cc4b9e0f9..159bd47be 100644 --- a/Jellyfin.Data/Entities/Libraries/Person.cs +++ b/Jellyfin.Data/Entities/Libraries/Person.cs @@ -46,7 +46,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Name { get; set; } @@ -59,7 +58,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(256)] [StringLength(256)] - public string SourceId { get; set; } + public string? SourceId { get; set; } /// /// Gets or sets the date added. diff --git a/Jellyfin.Data/Entities/Libraries/PersonRole.cs b/Jellyfin.Data/Entities/Libraries/PersonRole.cs index 3ae2e4a68..988aa84ba 100644 --- a/Jellyfin.Data/Entities/Libraries/PersonRole.cs +++ b/Jellyfin.Data/Entities/Libraries/PersonRole.cs @@ -17,9 +17,12 @@ namespace Jellyfin.Data.Entities.Libraries /// Initializes a new instance of the class. /// /// The role type. - public PersonRole(PersonRoleType type) + /// The person. + public PersonRole(PersonRoleType type, Person person) { Type = type; + Person = person; + Artwork = new HashSet(); Sources = new HashSet(); } @@ -40,7 +43,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Role { get; set; } + public string? Role { get; set; } /// /// Gets or sets the person's role type. @@ -60,7 +63,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required. /// - [Required] public virtual Person Person { get; set; } /// diff --git a/Jellyfin.Data/Entities/Libraries/Rating.cs b/Jellyfin.Data/Entities/Libraries/Rating.cs index 0ea933fd7..6862012a8 100644 --- a/Jellyfin.Data/Entities/Libraries/Rating.cs +++ b/Jellyfin.Data/Entities/Libraries/Rating.cs @@ -48,7 +48,7 @@ namespace Jellyfin.Data.Entities.Libraries /// Gets or sets the rating type. /// If this is null it's the internal user rating. /// - public virtual RatingSource RatingType { get; set; } + public virtual RatingSource? RatingType { get; set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/RatingSource.cs b/Jellyfin.Data/Entities/Libraries/RatingSource.cs index 7e1a5a8f4..ae0d806ff 100644 --- a/Jellyfin.Data/Entities/Libraries/RatingSource.cs +++ b/Jellyfin.Data/Entities/Libraries/RatingSource.cs @@ -37,7 +37,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Name { get; set; } + public string? Name { get; set; } /// /// Gets or sets the minimum value. @@ -62,7 +62,7 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Gets or sets the metadata source. /// - public virtual MetadataProviderId Source { get; set; } + public virtual MetadataProviderId? Source { get; set; } /// public void OnSavingChanges() diff --git a/Jellyfin.Data/Entities/Libraries/Release.cs b/Jellyfin.Data/Entities/Libraries/Release.cs index 1871e0f10..21d403979 100644 --- a/Jellyfin.Data/Entities/Libraries/Release.cs +++ b/Jellyfin.Data/Entities/Libraries/Release.cs @@ -45,7 +45,6 @@ namespace Jellyfin.Data.Entities.Libraries /// /// Required, Max length = 1024. /// - [Required] [MaxLength(1024)] [StringLength(1024)] public string Name { get; set; } diff --git a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs index 61714f909..da40a075f 100644 --- a/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeasonMetadata.cs @@ -24,6 +24,6 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } } } diff --git a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs index e1acd2d45..730deccae 100644 --- a/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs +++ b/Jellyfin.Data/Entities/Libraries/SeriesMetadata.cs @@ -30,7 +30,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Outline { get; set; } + public string? Outline { get; set; } /// /// Gets or sets the plot. @@ -40,7 +40,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(65535)] [StringLength(65535)] - public string Plot { get; set; } + public string? Plot { get; set; } /// /// Gets or sets the tagline. @@ -50,7 +50,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(1024)] [StringLength(1024)] - public string Tagline { get; set; } + public string? Tagline { get; set; } /// /// Gets or sets the country code. @@ -60,7 +60,7 @@ namespace Jellyfin.Data.Entities.Libraries /// [MaxLength(2)] [StringLength(2)] - public string Country { get; set; } + public string? Country { get; set; } /// /// Gets or sets a collection containing the networks. @@ -68,7 +68,6 @@ namespace Jellyfin.Data.Entities.Libraries public virtual ICollection Networks { get; protected set; } /// - [NotMapped] public ICollection Companies => Networks; } } diff --git a/Jellyfin.Data/Entities/Preference.cs b/Jellyfin.Data/Entities/Preference.cs index 40f2f8ede..a8813ab88 100644 --- a/Jellyfin.Data/Entities/Preference.cs +++ b/Jellyfin.Data/Entities/Preference.cs @@ -46,7 +46,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 65535. /// - [Required] [MaxLength(65535)] [StringLength(65535)] public string Value { get; set; } diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 28e12adde..9aa809164 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -51,6 +51,7 @@ namespace Jellyfin.Data.Entities PasswordResetProviderId = passwordResetProviderId; AccessSchedules = new HashSet(); + DisplayPreferences = new HashSet(); ItemDisplayPreferences = new HashSet(); // Groups = new HashSet(); Permissions = new HashSet(); @@ -92,7 +93,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string Username { get; set; } @@ -105,7 +105,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(65535)] [StringLength(65535)] - public string Password { get; set; } + public string? Password { get; set; } /// /// Gets or sets the user's easy password, or null if none is set. @@ -115,7 +115,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(65535)] [StringLength(65535)] - public string EasyPassword { get; set; } + public string? EasyPassword { get; set; } /// /// Gets or sets a value indicating whether the user must update their password. @@ -133,7 +133,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(255)] [StringLength(255)] - public string AudioLanguagePreference { get; set; } + public string? AudioLanguagePreference { get; set; } /// /// Gets or sets the authentication provider id. @@ -141,7 +141,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string AuthenticationProviderId { get; set; } @@ -152,7 +151,6 @@ namespace Jellyfin.Data.Entities /// /// Required, Max length = 255. /// - [Required] [MaxLength(255)] [StringLength(255)] public string PasswordResetProviderId { get; set; } @@ -209,7 +207,7 @@ namespace Jellyfin.Data.Entities /// [MaxLength(255)] [StringLength(255)] - public string SubtitleLanguagePreference { get; set; } + public string? SubtitleLanguagePreference { get; set; } /// /// Gets or sets a value indicating whether missing episodes should be displayed. @@ -304,7 +302,7 @@ namespace Jellyfin.Data.Entities /// Gets or sets the user's profile image. Can be null. /// // [ForeignKey("UserId")] - public virtual ImageInfo ProfileImage { get; set; } + public virtual ImageInfo? ProfileImage { get; set; } /// /// Gets or sets the user's display preferences. @@ -312,8 +310,7 @@ namespace Jellyfin.Data.Entities /// /// Required. /// - [Required] - public virtual DisplayPreferences DisplayPreferences { get; set; } + public virtual ICollection DisplayPreferences { get; set; } /// /// Gets or sets the level of sync play permissions this user has. diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index a8ac45645..a2b6f074e 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -5,6 +5,7 @@ false true true + enable true true true diff --git a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs index 0340248bb..aa6015caa 100644 --- a/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs +++ b/Jellyfin.Server.Implementations/Events/Consumers/Session/PlaybackStartLogger.cs @@ -86,7 +86,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session return name; } - private static string? GetPlaybackNotificationType(string mediaType) + private static string GetPlaybackNotificationType(string mediaType) { if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase)) { @@ -98,7 +98,7 @@ namespace Jellyfin.Server.Implementations.Events.Consumers.Session return NotificationType.VideoPlayback.ToString(); } - return null; + return "Playback"; } } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 76d1389ca..b400a0dd1 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -184,8 +184,8 @@ namespace Jellyfin.Server.Implementations.Users var user = new User( name, - _defaultAuthenticationProvider.GetType().FullName, - _defaultPasswordResetProvider.GetType().FullName) + _defaultAuthenticationProvider.GetType().FullName!, + _defaultPasswordResetProvider.GetType().FullName!) { InternalId = max + 1 }; @@ -444,7 +444,7 @@ namespace Jellyfin.Server.Implementations.Users { var providerId = authenticationProvider.GetType().FullName; - if (!string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase)) + if (providerId != null && !string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase)) { user.AuthenticationProviderId = providerId; await UpdateUserAsync(user).ConfigureAwait(false); diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs index 33f039c39..6d318d232 100644 --- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs +++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs @@ -1,7 +1,5 @@ using System; -using System.Globalization; using System.IO; -using System.Linq; using Emby.Server.Implementations.Data; using Emby.Server.Implementations.Serialization; using Jellyfin.Data.Entities; @@ -104,7 +102,7 @@ namespace Jellyfin.Server.Migrations.Routines _ => policy.LoginAttemptsBeforeLockout }; - var user = new User(mockup.Name, policy.AuthenticationProviderId, policy.PasswordResetProviderId) + var user = new User(mockup.Name, policy.AuthenticationProviderId!, policy.PasswordResetProviderId!) { Id = entry[1].ReadGuidFromBlob(), InternalId = entry[0].ToInt64(), diff --git a/MediaBrowser.Controller/Drawing/IImageProcessor.cs b/MediaBrowser.Controller/Drawing/IImageProcessor.cs index 935a79031..142cebd0c 100644 --- a/MediaBrowser.Controller/Drawing/IImageProcessor.cs +++ b/MediaBrowser.Controller/Drawing/IImageProcessor.cs @@ -61,7 +61,7 @@ namespace MediaBrowser.Controller.Drawing string GetImageCacheTag(BaseItem item, ChapterInfo info); - string GetImageCacheTag(User user); + string? GetImageCacheTag(User user); /// /// Processes the image. diff --git a/tests/Jellyfin.Api.Tests/TestHelpers.cs b/tests/Jellyfin.Api.Tests/TestHelpers.cs index f27cdf7b6..c1549561d 100644 --- a/tests/Jellyfin.Api.Tests/TestHelpers.cs +++ b/tests/Jellyfin.Api.Tests/TestHelpers.cs @@ -26,8 +26,8 @@ namespace Jellyfin.Api.Tests { var user = new User( "jellyfin", - typeof(DefaultAuthenticationProvider).FullName, - typeof(DefaultPasswordResetProvider).FullName); + typeof(DefaultAuthenticationProvider).FullName!, + typeof(DefaultPasswordResetProvider).FullName!); // Set administrator flag. user.SetPermission(PermissionKind.IsAdministrator, role.Equals(UserRoles.Administrator, StringComparison.OrdinalIgnoreCase)); -- cgit v1.2.3 From 12b8e29aefad3ad202769cdab9c6afc17c5687d9 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Wed, 17 Mar 2021 17:42:45 -0400 Subject: Fix duplicate permissions --- Jellyfin.Data/Entities/User.cs | 33 ++++++++++++---------- .../Users/UserManager.cs | 3 ++ 2 files changed, 21 insertions(+), 15 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Data/Entities/User.cs b/Jellyfin.Data/Entities/User.cs index 9aa809164..74331726c 100644 --- a/Jellyfin.Data/Entities/User.cs +++ b/Jellyfin.Data/Entities/User.cs @@ -73,9 +73,6 @@ namespace Jellyfin.Data.Entities PlayDefaultAudioTrack = true; SubtitleMode = SubtitlePlaybackMode.Default; SyncPlayAccess = SyncPlayUserAccessType.CreateAndJoinGroups; - - AddDefaultPermissions(); - AddDefaultPreferences(); } /// @@ -483,18 +480,11 @@ namespace Jellyfin.Data.Entities return Array.IndexOf(GetPreferenceValues(PreferenceKind.GroupedFolders), id) != -1; } - private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date) - { - var localTime = date.ToLocalTime(); - var hour = localTime.TimeOfDay.TotalHours; - - return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) - && hour >= schedule.StartHour - && hour <= schedule.EndHour; - } - + /// + /// Initializes the default permissions for a user. Should only be called on user creation. + /// // TODO: make these user configurable? - private void AddDefaultPermissions() + public void AddDefaultPermissions() { Permissions.Add(new Permission(PermissionKind.IsAdministrator, false)); Permissions.Add(new Permission(PermissionKind.IsDisabled, false)); @@ -519,12 +509,25 @@ namespace Jellyfin.Data.Entities Permissions.Add(new Permission(PermissionKind.EnableRemoteControlOfOtherUsers, false)); } - private void AddDefaultPreferences() + /// + /// Initializes the default preferences. Should only be called on user creation. + /// + public void AddDefaultPreferences() { foreach (var val in Enum.GetValues(typeof(PreferenceKind)).Cast()) { Preferences.Add(new Preference(val, string.Empty)); } } + + private static bool IsParentalScheduleAllowed(AccessSchedule schedule, DateTime date) + { + var localTime = date.ToLocalTime(); + var hour = localTime.TimeOfDay.TotalHours; + + return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) + && hour >= schedule.StartHour + && hour <= schedule.EndHour; + } } } diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index b400a0dd1..50d7612f2 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -190,6 +190,9 @@ namespace Jellyfin.Server.Implementations.Users InternalId = max + 1 }; + user.AddDefaultPermissions(); + user.AddDefaultPreferences(); + _users.Add(user.Id, user); return user; -- cgit v1.2.3 From a7b29e2fe0bb08f4b8f37fa5aac7af66c7cb00e8 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 25 Mar 2021 19:48:30 -0400 Subject: Clean up user renaming --- Jellyfin.Server.Implementations/Users/UserManager.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 50d7612f2..f9a1a8ee9 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -144,7 +144,13 @@ namespace Jellyfin.Server.Implementations.Users throw new ArgumentException("The new and old names must be different."); } - if (Users.Any(u => u.Id != user.Id && u.Username.Equals(newName, StringComparison.OrdinalIgnoreCase))) + await using var dbContext = _dbProvider.CreateContext(); + + if (await dbContext.Users + .AsQueryable() + .Where(u => u.Username == newName && u.Id != user.Id) + .AnyAsync() + .ConfigureAwait(false)) { throw new ArgumentException(string.Format( CultureInfo.InvariantCulture, -- cgit v1.2.3 From 7364155579c52e5019cdcb71edc03546babe3fb3 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Thu, 25 Mar 2021 19:49:52 -0400 Subject: Clean up user deletion --- Jellyfin.Server.Implementations/Users/UserManager.cs | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index f9a1a8ee9..b6b45e69b 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -257,16 +257,6 @@ namespace Jellyfin.Server.Implementations.Users } await using var dbContext = _dbProvider.CreateContext(); - - // Clear all entities related to the user from the database. - if (user.ProfileImage != null) - { - dbContext.Remove(user.ProfileImage); - } - - dbContext.RemoveRange(user.Permissions); - dbContext.RemoveRange(user.Preferences); - dbContext.RemoveRange(user.AccessSchedules); dbContext.Users.Remove(user); await dbContext.SaveChangesAsync().ConfigureAwait(false); _users.Remove(userId); -- cgit v1.2.3 From 3ebc0474343eb07defb6dd6f0e8bed707471e0a0 Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 10 Apr 2021 16:59:41 -0400 Subject: Convert UpdateUser to solely async --- .../LiveTv/Listings/SchedulesDirect.cs | 4 ++-- Emby.Server.Implementations/Session/SessionManager.cs | 2 +- Jellyfin.Server.Implementations/Users/UserManager.cs | 17 ++++------------- MediaBrowser.Controller/Library/IUserManager.cs | 16 ++++------------ 4 files changed, 11 insertions(+), 28 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index 1926e738f..dd58ca899 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -457,10 +457,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings } StringBuilder str = new StringBuilder("[", 1 + (programIds.Count * 13)); - foreach (ReadOnlySpan i in programIds) + foreach (string i in programIds) { str.Append('"') - .Append(i.Slice(0, 10)) + .Append(i[..10]) .Append("\","); } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 678a27665..50156d2b6 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -293,7 +293,7 @@ namespace Emby.Server.Implementations.Session try { user.LastActivityDate = activityDate; - _userManager.UpdateUser(user); + await _userManager.UpdateUserAsync(user); } catch (DbUpdateConcurrencyException e) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index b6b45e69b..4d847ec95 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -163,15 +163,6 @@ namespace Jellyfin.Server.Implementations.Users OnUserUpdated?.Invoke(this, new GenericEventArgs(user)); } - /// - public void UpdateUser(User user) - { - using var dbContext = _dbProvider.CreateContext(); - dbContext.Users.Update(user); - _users[user.Id] = user; - dbContext.SaveChanges(); - } - /// public async Task UpdateUserAsync(User user) { @@ -271,9 +262,9 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void ResetEasyPassword(User user) + public Task ResetEasyPassword(User user) { - ChangeEasyPassword(user, string.Empty, null); + return ChangeEasyPassword(user, string.Empty, null); } /// @@ -291,7 +282,7 @@ namespace Jellyfin.Server.Implementations.Users } /// - public void ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1) + public async Task ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1) { if (newPassword != null) { @@ -304,7 +295,7 @@ namespace Jellyfin.Server.Implementations.Users } user.EasyPassword = newPasswordSha1; - UpdateUser(user); + await UpdateUserAsync(user); _eventManager.Publish(new UserPasswordChangedEventArgs(user)); } diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs index 6e267834b..41dfe1b96 100644 --- a/MediaBrowser.Controller/Library/IUserManager.cs +++ b/MediaBrowser.Controller/Library/IUserManager.cs @@ -63,14 +63,6 @@ namespace MediaBrowser.Controller.Library /// Task RenameUser(User user, string newName); - /// - /// Updates the user. - /// - /// The user. - /// user - /// - void UpdateUser(User user); - /// /// Updates the user. /// @@ -108,7 +100,7 @@ namespace MediaBrowser.Controller.Library /// /// The user. /// Task. - void ResetEasyPassword(User user); + Task ResetEasyPassword(User user); /// /// Changes the password. @@ -118,7 +110,7 @@ namespace MediaBrowser.Controller.Library /// /// Changes the easy password. /// - void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1); + Task ChangeEasyPassword(User user, string newPassword, string newPasswordSha1); /// /// Gets the user dto. @@ -155,7 +147,7 @@ namespace MediaBrowser.Controller.Library /// /// This method updates the user's configuration. /// This is only included as a stopgap until the new API, using this internally is not recommended. - /// Instead, modify the user object directly, then call . + /// Instead, modify the user object directly, then call . /// /// The user's Id. /// The request containing the new user configuration. @@ -165,7 +157,7 @@ namespace MediaBrowser.Controller.Library /// /// This method updates the user's policy. /// This is only included as a stopgap until the new API, using this internally is not recommended. - /// Instead, modify the user object directly, then call . + /// Instead, modify the user object directly, then call . /// /// The user's Id. /// The request containing the new user policy. -- cgit v1.2.3 From ed0b5ff0171e340544702d99a07c149e01c5bf8a Mon Sep 17 00:00:00 2001 From: Patrick Barron Date: Sat, 10 Apr 2021 17:11:59 -0400 Subject: Fix builds --- Jellyfin.Data/Entities/Security/Device.cs | 5 +++++ Jellyfin.Server.Implementations/Devices/DeviceManager.cs | 9 ++++++--- .../Security/AuthenticationManager.cs | 7 ++++--- Jellyfin.Server.Implementations/Users/UserManager.cs | 2 +- MediaBrowser.Controller/Devices/IDeviceManager.cs | 8 ++++---- 5 files changed, 20 insertions(+), 11 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Jellyfin.Data/Entities/Security/Device.cs b/Jellyfin.Data/Entities/Security/Device.cs index 17d17f594..bb192e772 100644 --- a/Jellyfin.Data/Entities/Security/Device.cs +++ b/Jellyfin.Data/Entities/Security/Device.cs @@ -1,5 +1,6 @@ using System; using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; namespace Jellyfin.Data.Entities.Security { @@ -31,6 +32,10 @@ namespace Jellyfin.Data.Entities.Security User = null!; } + /// + /// Gets the id. + /// + [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; private set; } /// diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs index 0d93ee2bf..4758f24f3 100644 --- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs +++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs @@ -15,6 +15,9 @@ using Microsoft.EntityFrameworkCore; namespace Jellyfin.Server.Implementations.Devices { + /// + /// Manages the creation, updating, and retrieval of devices. + /// public class DeviceManager : IDeviceManager { private readonly JellyfinDbProvider _dbProvider; @@ -63,9 +66,9 @@ namespace Jellyfin.Server.Implementations.Devices } /// - public ClientCapabilities GetCapabilities(string id) + public ClientCapabilities GetCapabilities(string deviceId) { - return _capabilitiesMap.TryGetValue(id, out ClientCapabilities? result) + return _capabilitiesMap.TryGetValue(deviceId, out ClientCapabilities? result) ? result : new ClientCapabilities(); } @@ -112,7 +115,7 @@ namespace Jellyfin.Server.Implementations.Devices sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId)); } - var array = await sessions.Select(ToDeviceInfo).ToArrayAsync(); + var array = await sessions.Select(ToDeviceInfo).ToArrayAsync().ConfigureAwait(false); return new QueryResult(array); } diff --git a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs index 37b8ee6e0..ab76e2302 100644 --- a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs +++ b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs @@ -52,14 +52,15 @@ namespace Jellyfin.Server.Implementations.Security } /// - public async Task DeleteApiKey(Guid id) + public async Task DeleteApiKey(Guid accessToken) { await using var dbContext = _dbProvider.CreateContext(); var key = await dbContext.ApiKeys .AsQueryable() - .Where(apiKey => apiKey.AccessToken == id) - .FirstOrDefaultAsync(); + .Where(apiKey => apiKey.AccessToken == accessToken) + .FirstOrDefaultAsync() + .ConfigureAwait(false); if (key == null) { diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 4d847ec95..87d33330f 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -295,7 +295,7 @@ namespace Jellyfin.Server.Implementations.Users } user.EasyPassword = newPasswordSha1; - await UpdateUserAsync(user); + await UpdateUserAsync(user).ConfigureAwait(false); _eventManager.Publish(new UserPasswordChangedEventArgs(user)); } diff --git a/MediaBrowser.Controller/Devices/IDeviceManager.cs b/MediaBrowser.Controller/Devices/IDeviceManager.cs index 0df040794..4cdd8575e 100644 --- a/MediaBrowser.Controller/Devices/IDeviceManager.cs +++ b/MediaBrowser.Controller/Devices/IDeviceManager.cs @@ -18,16 +18,16 @@ namespace MediaBrowser.Controller.Devices /// /// Saves the capabilities. /// - /// The reported identifier. + /// The device id. /// The capabilities. - void SaveCapabilities(string reportedId, ClientCapabilities capabilities); + void SaveCapabilities(string deviceId, ClientCapabilities capabilities); /// /// Gets the capabilities. /// - /// The reported identifier. + /// The device id. /// ClientCapabilities. - ClientCapabilities GetCapabilities(string reportedId); + ClientCapabilities GetCapabilities(string deviceId); /// /// Gets the device information. -- cgit v1.2.3 From 017380f1ddccb46ce270f1d0df8e07d639ba3704 Mon Sep 17 00:00:00 2001 From: Cody Robibero Date: Mon, 4 Oct 2021 07:43:40 -0600 Subject: Reference dotnet6-rc1 packages --- Emby.Dlna/Emby.Dlna.csproj | 2 +- Emby.Server.Implementations/Emby.Server.Implementations.csproj | 10 +++++----- Jellyfin.Api/Jellyfin.Api.csproj | 4 ++-- Jellyfin.Data/Jellyfin.Data.csproj | 2 +- .../Jellyfin.Server.Implementations.csproj | 8 ++++---- .../Security/AuthorizationContext.cs | 2 +- Jellyfin.Server.Implementations/Users/UserManager.cs | 5 +++++ Jellyfin.Server/Jellyfin.Server.csproj | 8 ++++---- MediaBrowser.Common/MediaBrowser.Common.csproj | 4 ++-- MediaBrowser.Controller/MediaBrowser.Controller.csproj | 6 +++--- MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj | 4 ++-- MediaBrowser.Model/MediaBrowser.Model.csproj | 4 ++-- MediaBrowser.Providers/MediaBrowser.Providers.csproj | 6 +++--- tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj | 4 ++-- .../Jellyfin.Server.Integration.Tests.csproj | 6 +++--- tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj | 6 +++--- 16 files changed, 43 insertions(+), 38 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Dlna/Emby.Dlna.csproj b/Emby.Dlna/Emby.Dlna.csproj index 1d4e3b047..5348aed63 100644 --- a/Emby.Dlna/Emby.Dlna.csproj +++ b/Emby.Dlna/Emby.Dlna.csproj @@ -73,7 +73,7 @@ - + diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 428cad071..fb1972610 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -25,11 +25,11 @@ - - - - - + + + + + diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj index 1c451ef6c..f46c0cbd1 100644 --- a/Jellyfin.Api/Jellyfin.Api.csproj +++ b/Jellyfin.Api/Jellyfin.Api.csproj @@ -14,8 +14,8 @@ - - + + diff --git a/Jellyfin.Data/Jellyfin.Data.csproj b/Jellyfin.Data/Jellyfin.Data.csproj index 19aef704c..f1bfaa63e 100644 --- a/Jellyfin.Data/Jellyfin.Data.csproj +++ b/Jellyfin.Data/Jellyfin.Data.csproj @@ -35,7 +35,7 @@ - + diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj index 337f5cb82..d9e6d794b 100644 --- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj +++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj @@ -19,13 +19,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs index ba2cfc724..3ab043c64 100644 --- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs +++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs @@ -2,12 +2,12 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Net; using System.Threading.Tasks; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Net; using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; using Microsoft.Net.Http.Headers; namespace Jellyfin.Server.Implementations.Security diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 02377bfd7..704a6a84e 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -701,6 +701,11 @@ namespace Jellyfin.Server.Implementations.Users /// public async Task ClearProfileImageAsync(User user) { + if (user.ProfileImage == null) + { + return; + } + await using var dbContext = _dbProvider.CreateContext(); dbContext.Remove(user.ProfileImage); await dbContext.SaveChangesAsync().ConfigureAwait(false); diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj index 074d43fba..6603105fa 100644 --- a/Jellyfin.Server/Jellyfin.Server.csproj +++ b/Jellyfin.Server/Jellyfin.Server.csproj @@ -31,10 +31,10 @@ - - - - + + + + diff --git a/MediaBrowser.Common/MediaBrowser.Common.csproj b/MediaBrowser.Common/MediaBrowser.Common.csproj index 6358c0000..c87d58a14 100644 --- a/MediaBrowser.Common/MediaBrowser.Common.csproj +++ b/MediaBrowser.Common/MediaBrowser.Common.csproj @@ -19,8 +19,8 @@ - - + + diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 47cec7d77..997772c04 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -15,10 +15,10 @@ - - + + - + diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj index 30cfb904e..22bba2366 100644 --- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj +++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj @@ -24,8 +24,8 @@ - - + + diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index b0a12a9c9..0ac58e355 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -35,9 +35,9 @@ - + - + diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 29d6b01f2..71a3554fd 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -16,9 +16,9 @@ - - - + + + diff --git a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj index b52ea078a..8b581857f 100644 --- a/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj +++ b/tests/Jellyfin.Api.Tests/Jellyfin.Api.Tests.csproj @@ -15,8 +15,8 @@ - - + + diff --git a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj index 7939c7118..38687ae61 100644 --- a/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj +++ b/tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj @@ -9,14 +9,14 @@ - - + + - + diff --git a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj index b30e690a5..db24df240 100644 --- a/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj +++ b/tests/Jellyfin.Server.Tests/Jellyfin.Server.Tests.csproj @@ -10,13 +10,13 @@ - - + + - + -- cgit v1.2.3 From 1b6eb2ff2d2cc3973fa529c721cf50e3ad849646 Mon Sep 17 00:00:00 2001 From: Bond_009 Date: Tue, 26 Oct 2021 13:56:30 +0200 Subject: Enable nullable for more files --- Emby.Server.Implementations/Channels/ChannelManager.cs | 4 +--- Jellyfin.Server.Implementations/Users/UserManager.cs | 6 +----- MediaBrowser.Model/Channels/ChannelFeatures.cs | 8 +++++--- MediaBrowser.Model/Channels/ChannelQuery.cs | 5 ++--- .../Configuration/BaseApplicationConfiguration.cs | 7 +++---- MediaBrowser.Model/Configuration/LibraryOptions.cs | 13 ++++++------- MediaBrowser.Model/Configuration/UserConfiguration.cs | 5 ++--- MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs | 3 +-- MediaBrowser.Model/Dlna/DeviceIdentification.cs | 1 - MediaBrowser.Model/Dlna/ITranscoderSupport.cs | 1 - MediaBrowser.Model/Users/PinRedeemResult.cs | 5 +++-- 11 files changed, 24 insertions(+), 34 deletions(-) (limited to 'Jellyfin.Server.Implementations/Users/UserManager.cs') diff --git a/Emby.Server.Implementations/Channels/ChannelManager.cs b/Emby.Server.Implementations/Channels/ChannelManager.cs index 178f30de0..09aee602a 100644 --- a/Emby.Server.Implementations/Channels/ChannelManager.cs +++ b/Emby.Server.Implementations/Channels/ChannelManager.cs @@ -586,7 +586,7 @@ namespace Emby.Server.Implementations.Channels { var supportsLatest = provider is ISupportsLatestMedia; - return new ChannelFeatures + return new ChannelFeatures(channel.Name, channel.Id) { CanFilter = !features.MaxPageSize.HasValue, CanSearch = provider is ISearchableChannel, @@ -596,8 +596,6 @@ namespace Emby.Server.Implementations.Channels MediaTypes = features.MediaTypes.ToArray(), SupportsSortOrderToggle = features.SupportsSortOrderToggle, SupportsLatestMedia = supportsLatest, - Name = channel.Name, - Id = channel.Id.ToString("N", CultureInfo.InvariantCulture), SupportsContentDownloading = features.SupportsContentDownloading, AutoRefreshLevels = features.AutoRefreshLevels }; diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs index 704a6a84e..8ca6e8d21 100644 --- a/Jellyfin.Server.Implementations/Users/UserManager.cs +++ b/Jellyfin.Server.Implementations/Users/UserManager.cs @@ -530,11 +530,7 @@ namespace Jellyfin.Server.Implementations.Users } } - return new PinRedeemResult - { - Success = false, - UsersReset = Array.Empty() - }; + return new PinRedeemResult(); } /// diff --git a/MediaBrowser.Model/Channels/ChannelFeatures.cs b/MediaBrowser.Model/Channels/ChannelFeatures.cs index d925b78b6..1ca8e80a6 100644 --- a/MediaBrowser.Model/Channels/ChannelFeatures.cs +++ b/MediaBrowser.Model/Channels/ChannelFeatures.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 using System; @@ -7,11 +6,14 @@ namespace MediaBrowser.Model.Channels { public class ChannelFeatures { - public ChannelFeatures() + public ChannelFeatures(string name, Guid id) { MediaTypes = Array.Empty(); ContentTypes = Array.Empty(); DefaultSortFields = Array.Empty(); + + Name = name; + Id = id; } /// @@ -24,7 +26,7 @@ namespace MediaBrowser.Model.Channels /// Gets or sets the identifier. /// /// The identifier. - public string Id { get; set; } + public Guid Id { get; set; } /// /// Gets or sets a value indicating whether this instance can search. diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs index 59966127f..f9380ce3a 100644 --- a/MediaBrowser.Model/Channels/ChannelQuery.cs +++ b/MediaBrowser.Model/Channels/ChannelQuery.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 using System; @@ -13,13 +12,13 @@ namespace MediaBrowser.Model.Channels /// Gets or sets the fields to return within the items, in addition to basic information. /// /// The fields. - public ItemFields[] Fields { get; set; } + public ItemFields[]? Fields { get; set; } public bool? EnableImages { get; set; } public int? ImageTypeLimit { get; set; } - public ImageType[] EnableImageTypes { get; set; } + public ImageType[]? EnableImageTypes { get; set; } /// /// Gets or sets the user identifier. diff --git a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs index b00d2fffb..57759a7d3 100644 --- a/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs +++ b/MediaBrowser.Model/Configuration/BaseApplicationConfiguration.cs @@ -1,4 +1,3 @@ -#nullable disable using System; using System.Xml.Serialization; @@ -35,21 +34,21 @@ namespace MediaBrowser.Model.Configuration /// Gets or sets the cache path. /// /// The cache path. - public string CachePath { get; set; } + public string? CachePath { get; set; } /// /// Gets or sets the last known version that was ran using the configuration. /// /// The version from previous run. [XmlIgnore] - public Version PreviousVersion { get; set; } + public Version? PreviousVersion { get; set; } /// /// Gets or sets the stringified PreviousVersion to be stored/loaded, /// because System.Version itself isn't xml-serializable. /// /// String value of PreviousVersion. - public string PreviousVersionStr + public string? PreviousVersionStr { get => PreviousVersion?.ToString(); set diff --git a/MediaBrowser.Model/Configuration/LibraryOptions.cs b/MediaBrowser.Model/Configuration/LibraryOptions.cs index 24698360e..aae5359b1 100644 --- a/MediaBrowser.Model/Configuration/LibraryOptions.cs +++ b/MediaBrowser.Model/Configuration/LibraryOptions.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 using System; @@ -52,21 +51,21 @@ namespace MediaBrowser.Model.Configuration /// Gets or sets the preferred metadata language. /// /// The preferred metadata language. - public string PreferredMetadataLanguage { get; set; } + public string? PreferredMetadataLanguage { get; set; } /// /// Gets or sets the metadata country code. /// /// The metadata country code. - public string MetadataCountryCode { get; set; } + public string? MetadataCountryCode { get; set; } public string SeasonZeroDisplayName { get; set; } - public string[] MetadataSavers { get; set; } + public string[]? MetadataSavers { get; set; } public string[] DisabledLocalMetadataReaders { get; set; } - public string[] LocalMetadataReaderOrder { get; set; } + public string[]? LocalMetadataReaderOrder { get; set; } public string[] DisabledSubtitleFetchers { get; set; } @@ -76,7 +75,7 @@ namespace MediaBrowser.Model.Configuration public bool SkipSubtitlesIfAudioTrackMatches { get; set; } - public string[] SubtitleDownloadLanguages { get; set; } + public string[]? SubtitleDownloadLanguages { get; set; } public bool RequirePerfectSubtitleMatch { get; set; } @@ -84,7 +83,7 @@ namespace MediaBrowser.Model.Configuration public TypeOptions[] TypeOptions { get; set; } - public TypeOptions GetTypeOptions(string type) + public TypeOptions? GetTypeOptions(string type) { foreach (var options in TypeOptions) { diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 935e6cbe1..81359462c 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 using System; @@ -33,7 +32,7 @@ namespace MediaBrowser.Model.Configuration /// Gets or sets the audio language preference. /// /// The audio language preference. - public string AudioLanguagePreference { get; set; } + public string? AudioLanguagePreference { get; set; } /// /// Gets or sets a value indicating whether [play default audio track]. @@ -45,7 +44,7 @@ namespace MediaBrowser.Model.Configuration /// Gets or sets the subtitle language preference. /// /// The subtitle language preference. - public string SubtitleLanguagePreference { get; set; } + public string? SubtitleLanguagePreference { get; set; } public bool DisplayMissingEpisodes { get; set; } diff --git a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs index 8ad070dcb..07129d715 100644 --- a/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs +++ b/MediaBrowser.Model/Configuration/XbmcMetadataOptions.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 namespace MediaBrowser.Model.Configuration @@ -13,7 +12,7 @@ namespace MediaBrowser.Model.Configuration EnablePathSubstitution = true; } - public string UserId { get; set; } + public string? UserId { get; set; } public string ReleaseDateFormat { get; set; } diff --git a/MediaBrowser.Model/Dlna/DeviceIdentification.cs b/MediaBrowser.Model/Dlna/DeviceIdentification.cs index c511801f4..6625b7981 100644 --- a/MediaBrowser.Model/Dlna/DeviceIdentification.cs +++ b/MediaBrowser.Model/Dlna/DeviceIdentification.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 using System; diff --git a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs index d9bd094d9..a70ce44cc 100644 --- a/MediaBrowser.Model/Dlna/ITranscoderSupport.cs +++ b/MediaBrowser.Model/Dlna/ITranscoderSupport.cs @@ -1,4 +1,3 @@ -#nullable disable #pragma warning disable CS1591 namespace MediaBrowser.Model.Dlna diff --git a/MediaBrowser.Model/Users/PinRedeemResult.cs b/MediaBrowser.Model/Users/PinRedeemResult.cs index 7e4553bac..23fa631e8 100644 --- a/MediaBrowser.Model/Users/PinRedeemResult.cs +++ b/MediaBrowser.Model/Users/PinRedeemResult.cs @@ -1,6 +1,7 @@ -#nullable disable #pragma warning disable CS1591 +using System; + namespace MediaBrowser.Model.Users { public class PinRedeemResult @@ -15,6 +16,6 @@ namespace MediaBrowser.Model.Users /// Gets or sets the users reset. /// /// The users reset. - public string[] UsersReset { get; set; } + public string[] UsersReset { get; set; } = Array.Empty(); } } -- cgit v1.2.3