aboutsummaryrefslogtreecommitdiff
path: root/Jellyfin.Server.Implementations/Users
diff options
context:
space:
mode:
authorTim Eisele <Ghost_of_Stone@web.de>2026-08-20 18:35:26 +0200
committerGitHub <noreply@github.com>2026-08-20 18:35:26 +0200
commitfb50b4df8b5e6dec9567772aa8baecfbccb34124 (patch)
tree1c773cd19a387a0722fa3e205faced0f88ff6320 /Jellyfin.Server.Implementations/Users
parentc70933a23f45379557c941ebe1b98abff0cf3cb2 (diff)
Stop user updates from orphaning permission and preference rows (#17645)
* Stop user updates from orphaning permission and preference rows * Make UserId non-nullable * Remove unnecessary ToList * Update Jellyfin.Server.Implementations/Users/UserManager.cs Co-authored-by: Claus Vium <cvium@users.noreply.github.com> --------- Co-authored-by: Claus Vium <cvium@users.noreply.github.com>
Diffstat (limited to 'Jellyfin.Server.Implementations/Users')
-rw-r--r--Jellyfin.Server.Implementations/Users/UserManager.cs69
1 files changed, 56 insertions, 13 deletions
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index 932ced547a..fea6084267 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -225,19 +225,8 @@ namespace Jellyfin.Server.Implementations.Users
?? throw new ResourceNotFoundException(nameof(user.Id));
dbContext.Entry(dbUser).CurrentValues.SetValues(user);
- dbContext.Permissions.RemoveRange(dbUser.Permissions);
- dbUser.Permissions.Clear();
- foreach (var permission in user.Permissions)
- {
- dbUser.Permissions.Add(new Permission(permission.Kind, permission.Value));
- }
-
- dbContext.Preferences.RemoveRange(dbUser.Preferences);
- dbUser.Preferences.Clear();
- foreach (var preference in user.Preferences)
- {
- dbUser.Preferences.Add(new Preference(preference.Kind, preference.Value));
- }
+ SyncPermissions(dbUser, user.Permissions);
+ SyncPreferences(dbUser, user.Preferences);
dbUser.AccessSchedules.Clear();
foreach (var accessSchedule in user.AccessSchedules)
@@ -271,6 +260,60 @@ namespace Jellyfin.Server.Implementations.Users
}
}
+ private static void SyncPermissions(User dbUser, ICollection<Permission> source)
+ {
+ var incoming = new Dictionary<PermissionKind, bool>();
+ foreach (var permission in source)
+ {
+ incoming[permission.Kind] = permission.Value;
+ }
+
+ foreach (var existing in dbUser.Permissions)
+ {
+ if (incoming.Remove(existing.Kind, out var value))
+ {
+ // EF only marks the row modified if the value actually differs, so an update that
+ // touches nothing but the user row - a session activity stamp - writes no children.
+ existing.Value = value;
+ }
+ else
+ {
+ dbUser.Permissions.Remove(existing);
+ }
+ }
+
+ foreach (var (kind, value) in incoming)
+ {
+ dbUser.Permissions.Add(new Permission(kind, value));
+ }
+ }
+
+ private static void SyncPreferences(User dbUser, ICollection<Preference> source)
+ {
+ var incoming = new Dictionary<PreferenceKind, string>();
+ foreach (var preference in source)
+ {
+ incoming[preference.Kind] = preference.Value;
+ }
+
+ foreach (var existing in dbUser.Preferences)
+ {
+ if (incoming.Remove(existing.Kind, out var value))
+ {
+ existing.Value = value;
+ }
+ else
+ {
+ dbUser.Preferences.Remove(existing);
+ }
+ }
+
+ foreach (var (kind, value) in incoming)
+ {
+ dbUser.Preferences.Add(new Preference(kind, value));
+ }
+ }
+
internal async Task<User> CreateUserInternalAsync(string name, JellyfinDbContext dbContext)
{
// TODO: Remove after user item data is migrated.