< Summary - Jellyfin

Information
Class: Jellyfin.Server.Migrations.Routines.MigrateUserDb
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 114
Coverable lines: 114
Total lines: 221
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 26
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%210%
get_Id()100%210%
get_Name()100%210%
get_PerformOnNewInstall()100%210%
Perform()0%702260%

File(s)

/srv/git/jellyfin/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using Emby.Server.Implementations.Data;
 4using Jellyfin.Data.Entities;
 5using Jellyfin.Data.Enums;
 6using Jellyfin.Extensions.Json;
 7using Jellyfin.Server.Implementations;
 8using Jellyfin.Server.Implementations.Users;
 9using MediaBrowser.Controller;
 10using MediaBrowser.Controller.Entities;
 11using MediaBrowser.Model.Configuration;
 12using MediaBrowser.Model.Serialization;
 13using MediaBrowser.Model.Users;
 14using Microsoft.Data.Sqlite;
 15using Microsoft.EntityFrameworkCore;
 16using Microsoft.Extensions.Logging;
 17using JsonSerializer = System.Text.Json.JsonSerializer;
 18
 19namespace Jellyfin.Server.Migrations.Routines
 20{
 21    /// <summary>
 22    /// The migration routine for migrating the user database to EF Core.
 23    /// </summary>
 24    public class MigrateUserDb : IMigrationRoutine
 25    {
 26        private const string DbFilename = "users.db";
 27
 28        private readonly ILogger<MigrateUserDb> _logger;
 29        private readonly IServerApplicationPaths _paths;
 30        private readonly IDbContextFactory<JellyfinDbContext> _provider;
 31        private readonly IXmlSerializer _xmlSerializer;
 32
 33        /// <summary>
 34        /// Initializes a new instance of the <see cref="MigrateUserDb"/> class.
 35        /// </summary>
 36        /// <param name="logger">The logger.</param>
 37        /// <param name="paths">The server application paths.</param>
 38        /// <param name="provider">The database provider.</param>
 39        /// <param name="xmlSerializer">The xml serializer.</param>
 40        public MigrateUserDb(
 41            ILogger<MigrateUserDb> logger,
 42            IServerApplicationPaths paths,
 43            IDbContextFactory<JellyfinDbContext> provider,
 44            IXmlSerializer xmlSerializer)
 45        {
 046            _logger = logger;
 047            _paths = paths;
 048            _provider = provider;
 049            _xmlSerializer = xmlSerializer;
 050        }
 51
 52        /// <inheritdoc/>
 053        public Guid Id => Guid.Parse("5C4B82A2-F053-4009-BD05-B6FCAD82F14C");
 54
 55        /// <inheritdoc/>
 056        public string Name => "MigrateUserDatabase";
 57
 58        /// <inheritdoc/>
 059        public bool PerformOnNewInstall => false;
 60
 61        /// <inheritdoc/>
 62        public void Perform()
 63        {
 064            var dataPath = _paths.DataPath;
 065            _logger.LogInformation("Migrating the user database may take a while, do not stop Jellyfin.");
 66
 067            using (var connection = new SqliteConnection($"Filename={Path.Combine(dataPath, DbFilename)}"))
 68            {
 069                connection.Open();
 070                using var dbContext = _provider.CreateDbContext();
 71
 072                var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
 73
 074                dbContext.RemoveRange(dbContext.Users);
 075                dbContext.SaveChanges();
 76
 077                foreach (var entry in queryResult)
 78                {
 079                    UserMockup? mockup = JsonSerializer.Deserialize<UserMockup>(entry.GetStream(2), JsonDefaults.Options
 080                    if (mockup is null)
 81                    {
 82                        continue;
 83                    }
 84
 085                    var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, mockup.Name);
 86
 087                    var configPath = Path.Combine(userDataDir, "config.xml");
 088                    var config = File.Exists(configPath)
 089                        ? (UserConfiguration?)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), configPath) 
 090                        : new UserConfiguration();
 91
 092                    var policyPath = Path.Combine(userDataDir, "policy.xml");
 093                    var policy = File.Exists(policyPath)
 094                        ? (UserPolicy?)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), policyPath) ?? new UserPol
 095                        : new UserPolicy();
 096                    policy.AuthenticationProviderId = policy.AuthenticationProviderId?.Replace(
 097                        "Emby.Server.Implementations.Library",
 098                        "Jellyfin.Server.Implementations.Users",
 099                        StringComparison.Ordinal)
 0100                        ?? typeof(DefaultAuthenticationProvider).FullName;
 101
 0102                    policy.PasswordResetProviderId = typeof(DefaultPasswordResetProvider).FullName;
 0103                    int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch
 0104                    {
 0105                        -1 => null,
 0106                        0 => 3,
 0107                        _ => policy.LoginAttemptsBeforeLockout
 0108                    };
 109
 0110                    var user = new User(mockup.Name, policy.AuthenticationProviderId!, policy.PasswordResetProviderId!)
 0111                    {
 0112                        Id = entry.GetGuid(1),
 0113                        InternalId = entry.GetInt64(0),
 0114                        MaxParentalAgeRating = policy.MaxParentalRating,
 0115                        EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess,
 0116                        RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit,
 0117                        InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount,
 0118                        LoginAttemptsBeforeLockout = maxLoginAttempts,
 0119                        SubtitleMode = config.SubtitleMode,
 0120                        HidePlayedInLatest = config.HidePlayedInLatest,
 0121                        EnableLocalPassword = config.EnableLocalPassword,
 0122                        PlayDefaultAudioTrack = config.PlayDefaultAudioTrack,
 0123                        DisplayCollectionsView = config.DisplayCollectionsView,
 0124                        DisplayMissingEpisodes = config.DisplayMissingEpisodes,
 0125                        AudioLanguagePreference = config.AudioLanguagePreference,
 0126                        RememberAudioSelections = config.RememberAudioSelections,
 0127                        EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay,
 0128                        RememberSubtitleSelections = config.RememberSubtitleSelections,
 0129                        SubtitleLanguagePreference = config.SubtitleLanguagePreference,
 0130                        Password = mockup.Password,
 0131                        LastLoginDate = mockup.LastLoginDate,
 0132                        LastActivityDate = mockup.LastActivityDate
 0133                    };
 134
 0135                    if (mockup.ImageInfos.Length > 0)
 136                    {
 0137                        ItemImageInfo info = mockup.ImageInfos[0];
 138
 0139                        user.ProfileImage = new ImageInfo(info.Path)
 0140                        {
 0141                            LastModified = info.DateModified
 0142                        };
 143                    }
 144
 0145                    user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
 0146                    user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
 0147                    user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
 0148                    user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
 0149                    user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
 0150                    user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
 0151                    user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
 0152                    user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
 0153                    user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscod
 0154                    user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscod
 0155                    user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
 0156                    user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
 0157                    user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
 0158                    user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
 0159                    user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
 0160                    user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
 0161                    user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
 0162                    user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOther
 0163                    user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
 0164                    user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding)
 0165                    user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
 0166                    user.SetPermission(PermissionKind.EnableCollectionManagement, policy.EnableCollectionManagement);
 167
 0168                    foreach (var policyAccessSchedule in policy.AccessSchedules)
 169                    {
 0170                        user.AccessSchedules.Add(policyAccessSchedule);
 171                    }
 172
 0173                    user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
 0174                    user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
 0175                    user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
 0176                    user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
 0177                    user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFrom
 0178                    user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
 0179                    user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
 0180                    user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
 0181                    user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
 182
 0183                    dbContext.Users.Add(user);
 184                }
 185
 0186                dbContext.SaveChanges();
 187            }
 188
 189            try
 190            {
 0191                File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old"));
 192
 0193                var journalPath = Path.Combine(dataPath, DbFilename + "-journal");
 0194                if (File.Exists(journalPath))
 195                {
 0196                    File.Move(journalPath, Path.Combine(dataPath, DbFilename + ".old-journal"));
 197                }
 0198            }
 0199            catch (IOException e)
 200            {
 0201                _logger.LogError(e, "Error renaming legacy user database to 'users.db.old'");
 0202            }
 0203        }
 204
 205#nullable disable
 206        internal class UserMockup
 207        {
 208            public string Password { get; set; }
 209
 210            public string EasyPassword { get; set; }
 211
 212            public DateTime? LastLoginDate { get; set; }
 213
 214            public DateTime? LastActivityDate { get; set; }
 215
 216            public string Name { get; set; }
 217
 218            public ItemImageInfo[] ImageInfos { get; set; }
 219        }
 220    }
 221}