aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server.Implementations/Users
diff options
context:
space:
mode:
Diffstat (limited to 'Jellyfin.Server.Implementations/Users')
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs4
-rw-r--r--Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs8
-rw-r--r--Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs11
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs76
4 files changed, 35 insertions, 64 deletions
diff --git a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
index 7480a05c2..72f3d6e8e 100644
--- a/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultAuthenticationProvider.cs
@@ -41,7 +41,7 @@ namespace Jellyfin.Server.Implementations.Users
// This is the version that we need to use for local users. Because reasons.
public Task<ProviderAuthenticationResult> Authenticate(string username, string password, User resolvedUser)
{
- if (resolvedUser == null)
+ if (resolvedUser is null)
{
throw new AuthenticationException("Specified user does not exist.");
}
@@ -58,7 +58,7 @@ namespace Jellyfin.Server.Implementations.Users
}
// Handle the case when the stored password is null, but the user tried to login with a password
- if (resolvedUser.Password == null)
+ if (resolvedUser.Password is null)
{
throw new AuthenticationException("Invalid username or password");
}
diff --git a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
index 4fda8f5a4..cefbd0624 100644
--- a/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
+++ b/Jellyfin.Server.Implementations/Users/DefaultPasswordResetProvider.cs
@@ -54,7 +54,8 @@ namespace Jellyfin.Server.Implementations.Users
foreach (var resetFile in Directory.EnumerateFiles(_passwordResetFileBaseDir, $"{BaseResetFileName}*"))
{
SerializablePasswordReset spr;
- await using (var str = AsyncFile.OpenRead(resetFile))
+ var str = AsyncFile.OpenRead(resetFile);
+ await using (str.ConfigureAwait(false))
{
spr = await JsonSerializer.DeserializeAsync<SerializablePasswordReset>(str).ConfigureAwait(false)
?? throw new ResourceNotFoundException($"Provided path ({resetFile}) is not valid.");
@@ -107,13 +108,12 @@ namespace Jellyfin.Server.Implementations.Users
UserName = user.Username
};
- await using (FileStream fileStream = AsyncFile.OpenWrite(filePath))
+ FileStream fileStream = AsyncFile.OpenWrite(filePath);
+ await using (fileStream.ConfigureAwait(false))
{
await JsonSerializer.SerializeAsync(fileStream, spr).ConfigureAwait(false);
}
- user.EasyPassword = pin;
-
return new ForgotPasswordResult
{
Action = ForgotPasswordAction.PinCode,
diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
index 87babc05c..bfae81e4c 100644
--- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
+++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
@@ -15,13 +15,13 @@ namespace Jellyfin.Server.Implementations.Users
/// </summary>
public class DisplayPreferencesManager : IDisplayPreferencesManager
{
- private readonly JellyfinDb _dbContext;
+ private readonly JellyfinDbContext _dbContext;
/// <summary>
/// Initializes a new instance of the <see cref="DisplayPreferencesManager"/> class.
/// </summary>
/// <param name="dbContextFactory">The database context factory.</param>
- public DisplayPreferencesManager(IDbContextFactory<JellyfinDb> dbContextFactory)
+ public DisplayPreferencesManager(IDbContextFactory<JellyfinDbContext> dbContextFactory)
{
_dbContext = dbContextFactory.CreateDbContext();
}
@@ -34,7 +34,7 @@ namespace Jellyfin.Server.Implementations.Users
.FirstOrDefault(pref =>
pref.UserId.Equals(userId) && string.Equals(pref.Client, client) && pref.ItemId.Equals(itemId));
- if (prefs == null)
+ if (prefs is null)
{
prefs = new DisplayPreferences(userId, itemId, client);
_dbContext.DisplayPreferences.Add(prefs);
@@ -49,7 +49,7 @@ namespace Jellyfin.Server.Implementations.Users
var prefs = _dbContext.ItemDisplayPreferences
.FirstOrDefault(pref => pref.UserId.Equals(userId) && pref.ItemId.Equals(itemId) && string.Equals(pref.Client, client));
- if (prefs == null)
+ if (prefs is null)
{
prefs = new ItemDisplayPreferences(userId, Guid.Empty, client);
_dbContext.ItemDisplayPreferences.Add(prefs);
@@ -62,7 +62,6 @@ namespace Jellyfin.Server.Implementations.Users
public IList<ItemDisplayPreferences> ListItemDisplayPreferences(Guid userId, string client)
{
return _dbContext.ItemDisplayPreferences
- .AsQueryable()
.Where(prefs => prefs.UserId.Equals(userId) && !prefs.ItemId.Equals(default) && string.Equals(prefs.Client, client))
.ToList();
}
@@ -71,7 +70,6 @@ namespace Jellyfin.Server.Implementations.Users
public Dictionary<string, string?> ListCustomItemDisplayPreferences(Guid userId, Guid itemId, string client)
{
return _dbContext.CustomItemDisplayPreferences
- .AsQueryable()
.Where(prefs => prefs.UserId.Equals(userId)
&& prefs.ItemId.Equals(itemId)
&& string.Equals(prefs.Client, client))
@@ -82,7 +80,6 @@ namespace Jellyfin.Server.Implementations.Users
public void SetCustomItemDisplayPreferences(Guid userId, Guid itemId, string client, Dictionary<string, string?> customPreferences)
{
var existingPrefs = _dbContext.CustomItemDisplayPreferences
- .AsQueryable()
.Where(prefs => prefs.UserId.Equals(userId)
&& prefs.ItemId.Equals(itemId)
&& string.Equals(prefs.Client, client));
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index 25560707a..1d03baa4c 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -33,7 +33,7 @@ namespace Jellyfin.Server.Implementations.Users
/// </summary>
public class UserManager : IUserManager
{
- private readonly IDbContextFactory<JellyfinDb> _dbProvider;
+ private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
private readonly IEventManager _eventManager;
private readonly ICryptoProvider _cryptoProvider;
private readonly INetworkManager _networkManager;
@@ -59,7 +59,7 @@ namespace Jellyfin.Server.Implementations.Users
/// <param name="imageProcessor">The image processor.</param>
/// <param name="logger">The logger.</param>
public UserManager(
- IDbContextFactory<JellyfinDb> dbProvider,
+ IDbContextFactory<JellyfinDbContext> dbProvider,
IEventManager eventManager,
ICryptoProvider cryptoProvider,
INetworkManager networkManager,
@@ -85,6 +85,7 @@ namespace Jellyfin.Server.Implementations.Users
_users = new ConcurrentDictionary<Guid, User>();
using var dbContext = _dbProvider.CreateDbContext();
foreach (var user in dbContext.Users
+ .AsSplitQuery()
.Include(user => user.Permissions)
.Include(user => user.Preferences)
.Include(user => user.AccessSchedules)
@@ -143,7 +144,6 @@ namespace Jellyfin.Server.Implementations.Users
await using (dbContext.ConfigureAwait(false))
{
if (await dbContext.Users
- .AsQueryable()
.AnyAsync(u => u.Username == newName && !u.Id.Equals(user.Id))
.ConfigureAwait(false))
{
@@ -157,7 +157,9 @@ namespace Jellyfin.Server.Implementations.Users
await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false);
}
- OnUserUpdated?.Invoke(this, new GenericEventArgs<User>(user));
+ var eventArgs = new UserUpdatedEventArgs(user);
+ await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
+ OnUserUpdated?.Invoke(this, eventArgs);
}
/// <inheritdoc/>
@@ -170,7 +172,7 @@ namespace Jellyfin.Server.Implementations.Users
}
}
- internal async Task<User> CreateUserInternalAsync(string name, JellyfinDb dbContext)
+ internal async Task<User> CreateUserInternalAsync(string name, JellyfinDbContext dbContext)
{
// TODO: Remove after user item data is migrated.
var max = await dbContext.Users.AsQueryable().AnyAsync().ConfigureAwait(false)
@@ -267,36 +269,15 @@ namespace Jellyfin.Server.Implementations.Users
}
/// <inheritdoc/>
- public Task ResetEasyPassword(User user)
- {
- return ChangeEasyPassword(user, string.Empty, null);
- }
-
- /// <inheritdoc/>
public async Task ChangePassword(User user, string newPassword)
{
ArgumentNullException.ThrowIfNull(user);
-
- await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
- await UpdateUserAsync(user).ConfigureAwait(false);
-
- await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false);
- }
-
- /// <inheritdoc/>
- public async Task ChangeEasyPassword(User user, string newPassword, string? newPasswordSha1)
- {
- if (newPassword != null)
- {
- newPasswordSha1 = _cryptoProvider.CreatePasswordHash(newPassword).ToString();
- }
-
- if (string.IsNullOrWhiteSpace(newPasswordSha1))
+ if (user.HasPermission(PermissionKind.IsAdministrator) && string.IsNullOrWhiteSpace(newPassword))
{
- throw new ArgumentNullException(nameof(newPasswordSha1));
+ throw new ArgumentException("Admin user passwords must not be empty", nameof(newPassword));
}
- user.EasyPassword = newPasswordSha1;
+ await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
await UpdateUserAsync(user).ConfigureAwait(false);
await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false);
@@ -313,11 +294,10 @@ namespace Jellyfin.Server.Implementations.Users
ServerId = _appHost.SystemId,
HasPassword = hasPassword,
HasConfiguredPassword = hasPassword,
- HasConfiguredEasyPassword = !string.IsNullOrEmpty(user.EasyPassword),
EnableAutoLogin = user.EnableAutoLogin,
LastLoginDate = user.LastLoginDate,
LastActivityDate = user.LastActivityDate,
- PrimaryImageTag = user.ProfileImage != null ? _imageProcessor.GetImageCacheTag(user) : null,
+ PrimaryImageTag = user.ProfileImage is not null ? _imageProcessor.GetImageCacheTag(user) : null,
Configuration = new UserConfiguration
{
SubtitleMode = user.SubtitleMode,
@@ -367,8 +347,10 @@ namespace Jellyfin.Server.Implementations.Users
EnablePlaybackRemuxing = user.HasPermission(PermissionKind.EnablePlaybackRemuxing),
ForceRemoteSourceTranscoding = user.HasPermission(PermissionKind.ForceRemoteSourceTranscoding),
EnablePublicSharing = user.HasPermission(PermissionKind.EnablePublicSharing),
+ EnableCollectionManagement = user.HasPermission(PermissionKind.EnableCollectionManagement),
AccessSchedules = user.AccessSchedules.ToArray(),
BlockedTags = user.GetPreference(PreferenceKind.BlockedTags),
+ AllowedTags = user.GetPreference(PreferenceKind.AllowedTags),
EnabledChannels = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels),
EnabledDevices = user.GetPreference(PreferenceKind.EnabledDevices),
EnabledFolders = user.GetPreferenceValues<Guid>(PreferenceKind.EnabledFolders),
@@ -401,12 +383,12 @@ namespace Jellyfin.Server.Implementations.Users
var authenticationProvider = authResult.AuthenticationProvider;
var success = authResult.Success;
- if (user == null)
+ if (user is null)
{
string updatedUsername = authResult.Username;
if (success
- && authenticationProvider != null
+ && authenticationProvider is not null
&& authenticationProvider is not DefaultAuthenticationProvider)
{
// Trust the username returned by the authentication provider
@@ -416,25 +398,25 @@ 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 && user != null)
+ if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy && user is not null)
{
await UpdatePolicyAsync(user.Id, hasNewUserPolicy.GetNewUserPolicy()).ConfigureAwait(false);
}
}
}
- if (success && user != null && authenticationProvider != null)
+ if (success && user is not null && authenticationProvider is not null)
{
var providerId = authenticationProvider.GetType().FullName;
- if (providerId != null && !string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
+ if (providerId is not null && !string.Equals(providerId, user.AuthenticationProviderId, StringComparison.OrdinalIgnoreCase))
{
user.AuthenticationProviderId = providerId;
await UpdateUserAsync(user).ConfigureAwait(false);
}
}
- if (user == null)
+ if (user is null)
{
_logger.LogInformation(
"Authentication request for {UserName} has been denied (IP: {IP}).",
@@ -501,7 +483,7 @@ namespace Jellyfin.Server.Implementations.Users
{
var user = string.IsNullOrWhiteSpace(enteredUsername) ? null : GetUserByName(enteredUsername);
- if (user != null && isInNetwork)
+ if (user is not null && isInNetwork)
{
var passwordResetProvider = GetPasswordResetProvider(user);
var result = await passwordResetProvider
@@ -682,6 +664,7 @@ namespace Jellyfin.Server.Implementations.Users
user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
+ user.SetPermission(PermissionKind.EnableCollectionManagement, policy.EnableCollectionManagement);
user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
@@ -694,6 +677,7 @@ namespace Jellyfin.Server.Implementations.Users
// TODO: fix this at some point
user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty<UnratedItem>());
user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
+ user.SetPreference(PreferenceKind.AllowedTags, policy.AllowedTags);
user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
@@ -708,7 +692,7 @@ namespace Jellyfin.Server.Implementations.Users
/// <inheritdoc/>
public async Task ClearProfileImageAsync(User user)
{
- if (user.ProfileImage == null)
+ if (user.ProfileImage is null)
{
return;
}
@@ -734,7 +718,7 @@ namespace Jellyfin.Server.Implementations.Users
throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)", nameof(name));
}
- private static bool IsValidUsername(string name)
+ private static bool IsValidUsername(ReadOnlySpan<char> 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
@@ -826,16 +810,6 @@ namespace Jellyfin.Server.Implementations.Users
}
}
- if (!success
- && _networkManager.IsInLocalNetwork(remoteEndPoint)
- && user?.EnableLocalPassword == true
- && !string.IsNullOrEmpty(user.EasyPassword))
- {
- // Check easy password
- var passwordHash = PasswordHash.Parse(user.EasyPassword);
- success = _cryptoProvider.Verify(passwordHash, password);
- }
-
return (authenticationProvider, username, success);
}
@@ -884,7 +858,7 @@ namespace Jellyfin.Server.Implementations.Users
await UpdateUserAsync(user).ConfigureAwait(false);
}
- private async Task UpdateUserInternalAsync(JellyfinDb dbContext, User user)
+ private async Task UpdateUserInternalAsync(JellyfinDbContext dbContext, User user)
{
dbContext.Users.Update(user);
_users[user.Id] = user;