MigrateAuthenticationDb.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using Emby.Server.Implementations.Data;
  5. using Jellyfin.Data.Entities.Security;
  6. using Jellyfin.Server.Implementations;
  7. using MediaBrowser.Controller;
  8. using MediaBrowser.Controller.Library;
  9. using Microsoft.Data.Sqlite;
  10. using Microsoft.EntityFrameworkCore;
  11. using Microsoft.Extensions.Logging;
  12. namespace Jellyfin.Server.Migrations.Routines
  13. {
  14. /// <summary>
  15. /// A migration that moves data from the authentication database into the new schema.
  16. /// </summary>
  17. public class MigrateAuthenticationDb : IMigrationRoutine
  18. {
  19. private const string DbFilename = "authentication.db";
  20. private readonly ILogger<MigrateAuthenticationDb> _logger;
  21. private readonly IDbContextFactory<JellyfinDbContext> _dbProvider;
  22. private readonly IServerApplicationPaths _appPaths;
  23. private readonly IUserManager _userManager;
  24. /// <summary>
  25. /// Initializes a new instance of the <see cref="MigrateAuthenticationDb"/> class.
  26. /// </summary>
  27. /// <param name="logger">The logger.</param>
  28. /// <param name="dbProvider">The database provider.</param>
  29. /// <param name="appPaths">The server application paths.</param>
  30. /// <param name="userManager">The user manager.</param>
  31. public MigrateAuthenticationDb(
  32. ILogger<MigrateAuthenticationDb> logger,
  33. IDbContextFactory<JellyfinDbContext> dbProvider,
  34. IServerApplicationPaths appPaths,
  35. IUserManager userManager)
  36. {
  37. _logger = logger;
  38. _dbProvider = dbProvider;
  39. _appPaths = appPaths;
  40. _userManager = userManager;
  41. }
  42. /// <inheritdoc />
  43. public Guid Id => Guid.Parse("5BD72F41-E6F3-4F60-90AA-09869ABE0E22");
  44. /// <inheritdoc />
  45. public string Name => "MigrateAuthenticationDatabase";
  46. /// <inheritdoc />
  47. public bool PerformOnNewInstall => false;
  48. /// <inheritdoc />
  49. public void Perform()
  50. {
  51. var dataPath = _appPaths.DataPath;
  52. using (var connection = new SqliteConnection($"Filename={Path.Combine(dataPath, DbFilename)}"))
  53. {
  54. connection.Open();
  55. using var dbContext = _dbProvider.CreateDbContext();
  56. var authenticatedDevices = connection.Query("SELECT * FROM Tokens");
  57. foreach (var row in authenticatedDevices)
  58. {
  59. var dateCreatedStr = row.GetString(9);
  60. _ = DateTime.TryParse(dateCreatedStr, out var dateCreated);
  61. var dateLastActivityStr = row.GetString(10);
  62. _ = DateTime.TryParse(dateLastActivityStr, out var dateLastActivity);
  63. if (row.IsDBNull(6))
  64. {
  65. dbContext.ApiKeys.Add(new ApiKey(row.GetString(3))
  66. {
  67. AccessToken = row.GetString(1),
  68. DateCreated = dateCreated,
  69. DateLastActivity = dateLastActivity
  70. });
  71. }
  72. else
  73. {
  74. var userId = row.GetGuid(6);
  75. var user = _userManager.GetUserById(userId);
  76. if (user is null)
  77. {
  78. // User doesn't exist, don't bring over the device.
  79. continue;
  80. }
  81. dbContext.Devices.Add(new Device(
  82. userId,
  83. row.GetString(3),
  84. row.GetString(4),
  85. row.GetString(5),
  86. row.GetString(2))
  87. {
  88. AccessToken = row.GetString(1),
  89. IsActive = row.GetBoolean(8),
  90. DateCreated = dateCreated,
  91. DateLastActivity = dateLastActivity
  92. });
  93. }
  94. }
  95. var deviceOptions = connection.Query("SELECT * FROM Devices");
  96. var deviceIds = new HashSet<string>();
  97. foreach (var row in deviceOptions)
  98. {
  99. if (row.IsDBNull(2))
  100. {
  101. continue;
  102. }
  103. var deviceId = row.GetString(2);
  104. if (deviceIds.Contains(deviceId))
  105. {
  106. continue;
  107. }
  108. deviceIds.Add(deviceId);
  109. dbContext.DeviceOptions.Add(new DeviceOptions(deviceId)
  110. {
  111. CustomName = row.IsDBNull(1) ? null : row.GetString(1)
  112. });
  113. }
  114. dbContext.SaveChanges();
  115. }
  116. try
  117. {
  118. File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old"));
  119. var journalPath = Path.Combine(dataPath, DbFilename + "-journal");
  120. if (File.Exists(journalPath))
  121. {
  122. File.Move(journalPath, Path.Combine(dataPath, DbFilename + ".old-journal"));
  123. }
  124. }
  125. catch (IOException e)
  126. {
  127. _logger.LogError(e, "Error renaming legacy activity log database to 'authentication.db.old'");
  128. }
  129. }
  130. }
  131. }