mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
Migrate User DB to EF Core
This commit is contained in:
parent
aca7e221d8
commit
3eeb6576d8
@ -562,7 +562,6 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
|
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
|
||||||
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
|
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
|
||||||
serviceCollection.AddSingleton<IUserManager, UserManager>();
|
|
||||||
|
|
||||||
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
|
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
|
||||||
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
|
// TODO: Add StartupOptions.FFmpegPath to IConfiguration and remove this custom activation
|
||||||
|
@ -0,0 +1,225 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Threading;
|
||||||
|
using MediaBrowser.Common.Configuration;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
|
using MediaBrowser.Common.Json;
|
||||||
|
using MediaBrowser.Controller.Persistence;
|
||||||
|
using MediaBrowser.Model.Entities;
|
||||||
|
using MediaBrowser.Model.IO;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using SQLitePCL.pretty;
|
||||||
|
|
||||||
|
namespace Emby.Server.Implementations.Data
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Class SQLiteDisplayPreferencesRepository.
|
||||||
|
/// </summary>
|
||||||
|
public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository
|
||||||
|
{
|
||||||
|
private readonly IFileSystem _fileSystem;
|
||||||
|
|
||||||
|
private readonly JsonSerializerOptions _jsonOptions;
|
||||||
|
|
||||||
|
public SqliteDisplayPreferencesRepository(ILogger<SqliteDisplayPreferencesRepository> logger, IApplicationPaths appPaths, IFileSystem fileSystem)
|
||||||
|
: base(logger)
|
||||||
|
{
|
||||||
|
_fileSystem = fileSystem;
|
||||||
|
|
||||||
|
_jsonOptions = JsonDefaults.GetOptions();
|
||||||
|
|
||||||
|
DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the name of the repository.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The name.</value>
|
||||||
|
public string Name => "SQLite";
|
||||||
|
|
||||||
|
public void Initialize()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
InitializeInternal();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError(ex, "Error loading database file. Will reset and retry.");
|
||||||
|
|
||||||
|
_fileSystem.DeleteFile(DbFilePath);
|
||||||
|
|
||||||
|
InitializeInternal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens the connection to the database
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
private void InitializeInternal()
|
||||||
|
{
|
||||||
|
string[] queries =
|
||||||
|
{
|
||||||
|
"create table if not exists userdisplaypreferences (id GUID NOT NULL, userId GUID NOT NULL, client text NOT NULL, data BLOB NOT NULL)",
|
||||||
|
"create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)"
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var connection = GetConnection())
|
||||||
|
{
|
||||||
|
connection.RunQueries(queries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save the display preferences associated with an item in the repo
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayPreferences">The display preferences.</param>
|
||||||
|
/// <param name="userId">The user id.</param>
|
||||||
|
/// <param name="client">The client.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">item</exception>
|
||||||
|
public void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (displayPreferences == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(displayPreferences));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(displayPreferences.Id))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Display preferences has an invalid Id", nameof(displayPreferences));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
using (var connection = GetConnection())
|
||||||
|
{
|
||||||
|
connection.RunInTransaction(
|
||||||
|
db => SaveDisplayPreferences(displayPreferences, userId, client, db),
|
||||||
|
TransactionMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection)
|
||||||
|
{
|
||||||
|
var serialized = JsonSerializer.SerializeToUtf8Bytes(displayPreferences, _jsonOptions);
|
||||||
|
|
||||||
|
using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
|
||||||
|
{
|
||||||
|
statement.TryBind("@id", new Guid(displayPreferences.Id).ToByteArray());
|
||||||
|
statement.TryBind("@userId", userId.ToByteArray());
|
||||||
|
statement.TryBind("@client", client);
|
||||||
|
statement.TryBind("@data", serialized);
|
||||||
|
|
||||||
|
statement.MoveNext();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Save all display preferences associated with a user in the repo
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayPreferences">The display preferences.</param>
|
||||||
|
/// <param name="userId">The user id.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <exception cref="ArgumentNullException">item</exception>
|
||||||
|
public void SaveAllDisplayPreferences(IEnumerable<DisplayPreferences> displayPreferences, Guid userId, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (displayPreferences == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(displayPreferences));
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
using (var connection = GetConnection())
|
||||||
|
{
|
||||||
|
connection.RunInTransaction(
|
||||||
|
db =>
|
||||||
|
{
|
||||||
|
foreach (var displayPreference in displayPreferences)
|
||||||
|
{
|
||||||
|
SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
TransactionMode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the display preferences.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayPreferencesId">The display preferences id.</param>
|
||||||
|
/// <param name="userId">The user id.</param>
|
||||||
|
/// <param name="client">The client.</param>
|
||||||
|
/// <returns>Task{DisplayPreferences}.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">item</exception>
|
||||||
|
public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(displayPreferencesId))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(displayPreferencesId));
|
||||||
|
}
|
||||||
|
|
||||||
|
var guidId = displayPreferencesId.GetMD5();
|
||||||
|
|
||||||
|
using (var connection = GetConnection(true))
|
||||||
|
{
|
||||||
|
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
|
||||||
|
{
|
||||||
|
statement.TryBind("@id", guidId.ToByteArray());
|
||||||
|
statement.TryBind("@userId", userId.ToByteArray());
|
||||||
|
statement.TryBind("@client", client);
|
||||||
|
|
||||||
|
foreach (var row in statement.ExecuteQuery())
|
||||||
|
{
|
||||||
|
return Get(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new DisplayPreferences
|
||||||
|
{
|
||||||
|
Id = guidId.ToString("N", CultureInfo.InvariantCulture)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all display preferences for the given user.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="userId">The user id.</param>
|
||||||
|
/// <returns>Task{DisplayPreferences}.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">item</exception>
|
||||||
|
public IEnumerable<DisplayPreferences> GetAllDisplayPreferences(Guid userId)
|
||||||
|
{
|
||||||
|
var list = new List<DisplayPreferences>();
|
||||||
|
|
||||||
|
using (var connection = GetConnection(true))
|
||||||
|
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
|
||||||
|
{
|
||||||
|
statement.TryBind("@userId", userId.ToByteArray());
|
||||||
|
|
||||||
|
foreach (var row in statement.ExecuteQuery())
|
||||||
|
{
|
||||||
|
list.Add(Get(row));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DisplayPreferences Get(IReadOnlyList<IResultSetValue> row)
|
||||||
|
=> JsonSerializer.Deserialize<DisplayPreferences>(row[0].ToBlob(), _jsonOptions);
|
||||||
|
|
||||||
|
public void SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken)
|
||||||
|
=> SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken);
|
||||||
|
|
||||||
|
public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client)
|
||||||
|
=> GetDisplayPreferences(displayPreferencesId, new Guid(userId), client);
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ using System.Globalization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Common.Events;
|
using MediaBrowser.Common.Events;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
@ -14,7 +15,6 @@ using MediaBrowser.Controller.Devices;
|
|||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.TV;
|
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Controller.Security;
|
using MediaBrowser.Controller.Security;
|
||||||
@ -27,6 +27,7 @@ using MediaBrowser.Model.Library;
|
|||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Session
|
namespace Emby.Server.Implementations.Session
|
||||||
{
|
{
|
||||||
@ -254,7 +255,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
string deviceId,
|
string deviceId,
|
||||||
string deviceName,
|
string deviceName,
|
||||||
string remoteEndPoint,
|
string remoteEndPoint,
|
||||||
Jellyfin.Data.Entities.User user)
|
User user)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
@ -438,7 +439,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
string deviceId,
|
string deviceId,
|
||||||
string deviceName,
|
string deviceName,
|
||||||
string remoteEndPoint,
|
string remoteEndPoint,
|
||||||
Jellyfin.Data.Entities.User user)
|
User user)
|
||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
@ -457,7 +458,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
sessionInfo.UserId = user?.Id ?? Guid.Empty;
|
sessionInfo.UserId = user?.Id ?? Guid.Empty;
|
||||||
sessionInfo.UserName = user?.Username;
|
sessionInfo.UserName = user?.Username;
|
||||||
sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user);
|
sessionInfo.UserPrimaryImageTag = user?.ProfileImage == null ? null : GetImageCacheTag(user);
|
||||||
sessionInfo.RemoteEndPoint = remoteEndPoint;
|
sessionInfo.RemoteEndPoint = remoteEndPoint;
|
||||||
sessionInfo.Client = appName;
|
sessionInfo.Client = appName;
|
||||||
|
|
||||||
@ -483,7 +484,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
string deviceId,
|
string deviceId,
|
||||||
string deviceName,
|
string deviceName,
|
||||||
string remoteEndPoint,
|
string remoteEndPoint,
|
||||||
Jellyfin.Data.Entities.User user)
|
User user)
|
||||||
{
|
{
|
||||||
var sessionInfo = new SessionInfo(this, _logger)
|
var sessionInfo = new SessionInfo(this, _logger)
|
||||||
{
|
{
|
||||||
@ -497,7 +498,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
sessionInfo.UserId = user?.Id ?? Guid.Empty;
|
sessionInfo.UserId = user?.Id ?? Guid.Empty;
|
||||||
sessionInfo.UserName = username;
|
sessionInfo.UserName = username;
|
||||||
sessionInfo.UserPrimaryImageTag = user == null ? null : GetImageCacheTag(user);
|
sessionInfo.UserPrimaryImageTag = user?.ProfileImage == null ? null : GetImageCacheTag(user);
|
||||||
sessionInfo.RemoteEndPoint = remoteEndPoint;
|
sessionInfo.RemoteEndPoint = remoteEndPoint;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(deviceName))
|
if (string.IsNullOrEmpty(deviceName))
|
||||||
@ -520,9 +521,9 @@ namespace Emby.Server.Implementations.Session
|
|||||||
return sessionInfo;
|
return sessionInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Jellyfin.Data.Entities.User> GetUsers(SessionInfo session)
|
private List<User> GetUsers(SessionInfo session)
|
||||||
{
|
{
|
||||||
var users = new List<Jellyfin.Data.Entities.User>();
|
var users = new List<User>();
|
||||||
|
|
||||||
if (session.UserId != Guid.Empty)
|
if (session.UserId != Guid.Empty)
|
||||||
{
|
{
|
||||||
@ -680,7 +681,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The user object.</param>
|
/// <param name="user">The user object.</param>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
private void OnPlaybackStart(Jellyfin.Data.Entities.User user, BaseItem item)
|
private void OnPlaybackStart(User user, BaseItem item)
|
||||||
{
|
{
|
||||||
var data = _userDataManager.GetUserData(user, item);
|
var data = _userDataManager.GetUserData(user, item);
|
||||||
|
|
||||||
@ -763,7 +764,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
StartIdleCheckTimer();
|
StartIdleCheckTimer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPlaybackProgress(Jellyfin.Data.Entities.User user, BaseItem item, PlaybackProgressInfo info)
|
private void OnPlaybackProgress(User user, BaseItem item, PlaybackProgressInfo info)
|
||||||
{
|
{
|
||||||
var data = _userDataManager.GetUserData(user, item);
|
var data = _userDataManager.GetUserData(user, item);
|
||||||
|
|
||||||
@ -789,7 +790,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool UpdatePlaybackSettings(Jellyfin.Data.Entities.User user, PlaybackProgressInfo info, UserItemData data)
|
private static bool UpdatePlaybackSettings(User user, PlaybackProgressInfo info, UserItemData data)
|
||||||
{
|
{
|
||||||
var changed = false;
|
var changed = false;
|
||||||
|
|
||||||
@ -949,7 +950,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
_logger);
|
_logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool OnPlaybackStopped(Jellyfin.Data.Entities.User user, BaseItem item, long? positionTicks, bool playbackFailed)
|
private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)
|
||||||
{
|
{
|
||||||
bool playedToCompletion = false;
|
bool playedToCompletion = false;
|
||||||
|
|
||||||
@ -1163,7 +1164,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
|
await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, Jellyfin.Data.Entities.User user)
|
private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(id);
|
var item = _libraryManager.GetItemById(id);
|
||||||
|
|
||||||
@ -1216,7 +1217,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
return new[] { item };
|
return new[] { item };
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, Jellyfin.Data.Entities.User user)
|
private IEnumerable<BaseItem> TranslateItemForInstantMix(Guid id, User user)
|
||||||
{
|
{
|
||||||
var item = _libraryManager.GetItemById(id);
|
var item = _libraryManager.GetItemById(id);
|
||||||
|
|
||||||
@ -1399,7 +1400,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
{
|
{
|
||||||
CheckDisposed();
|
CheckDisposed();
|
||||||
|
|
||||||
Jellyfin.Data.Entities.User user = null;
|
User user = null;
|
||||||
if (request.UserId != Guid.Empty)
|
if (request.UserId != Guid.Empty)
|
||||||
{
|
{
|
||||||
user = _userManager.GetUserById(request.UserId);
|
user = _userManager.GetUserById(request.UserId);
|
||||||
@ -1455,7 +1456,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
return returnResult;
|
return returnResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetAuthorizationToken(Jellyfin.Data.Entities.User user, string deviceId, string app, string appVersion, string deviceName)
|
private string GetAuthorizationToken(User user, string deviceId, string app, string appVersion, string deviceName)
|
||||||
{
|
{
|
||||||
var existing = _authRepo.Get(
|
var existing = _authRepo.Get(
|
||||||
new AuthenticationInfoQuery
|
new AuthenticationInfoQuery
|
||||||
@ -1701,7 +1702,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetImageCacheTag(Jellyfin.Data.Entities.User user)
|
private string GetImageCacheTag(User user)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
|
|
||||||
namespace Jellyfin.Data.Entities
|
namespace Jellyfin.Data.Entities
|
||||||
@ -20,8 +22,9 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// <param name="dayOfWeek">The day of the week.</param>
|
/// <param name="dayOfWeek">The day of the week.</param>
|
||||||
/// <param name="startHour">The start hour.</param>
|
/// <param name="startHour">The start hour.</param>
|
||||||
/// <param name="endHour">The end hour.</param>
|
/// <param name="endHour">The end hour.</param>
|
||||||
public AccessSchedule(DynamicDayOfWeek dayOfWeek, double startHour, double endHour)
|
public AccessSchedule(DynamicDayOfWeek dayOfWeek, double startHour, double endHour, Guid userId)
|
||||||
{
|
{
|
||||||
|
UserId = userId;
|
||||||
DayOfWeek = dayOfWeek;
|
DayOfWeek = dayOfWeek;
|
||||||
StartHour = startHour;
|
StartHour = startHour;
|
||||||
EndHour = endHour;
|
EndHour = endHour;
|
||||||
@ -34,15 +37,20 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// <param name="startHour">The start hour.</param>
|
/// <param name="startHour">The start hour.</param>
|
||||||
/// <param name="endHour">The end hour.</param>
|
/// <param name="endHour">The end hour.</param>
|
||||||
/// <returns>The newly created instance.</returns>
|
/// <returns>The newly created instance.</returns>
|
||||||
public static AccessSchedule CreateInstance(DynamicDayOfWeek dayOfWeek, double startHour, double endHour)
|
public static AccessSchedule CreateInstance(DynamicDayOfWeek dayOfWeek, double startHour, double endHour, Guid userId)
|
||||||
{
|
{
|
||||||
return new AccessSchedule(dayOfWeek, startHour, endHour);
|
return new AccessSchedule(dayOfWeek, startHour, endHour, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
[Key]
|
[Key]
|
||||||
[Required]
|
[Required]
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
public int Id { get; protected set; }
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
[ForeignKey("Id")]
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the day of week.
|
/// Gets or sets the day of week.
|
||||||
|
@ -5,7 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema;
|
|||||||
|
|
||||||
namespace Jellyfin.Data.Entities
|
namespace Jellyfin.Data.Entities
|
||||||
{
|
{
|
||||||
public partial class Group
|
public partial class Group : IHasPermissions, ISavingChanges
|
||||||
{
|
{
|
||||||
partial void Init();
|
partial void Init();
|
||||||
|
|
||||||
@ -14,35 +14,29 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected Group()
|
protected Group()
|
||||||
{
|
{
|
||||||
GroupPermissions = new HashSet<Permission>();
|
Permissions = new HashSet<Permission>();
|
||||||
ProviderMappings = new HashSet<ProviderMapping>();
|
ProviderMappings = new HashSet<ProviderMapping>();
|
||||||
Preferences = new HashSet<Preference>();
|
Preferences = new HashSet<Preference>();
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
|
||||||
/// </summary>
|
|
||||||
public static Group CreateGroupUnsafe()
|
|
||||||
{
|
|
||||||
return new Group();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Public constructor with required data
|
/// Public constructor with required data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name"></param>
|
/// <param name="name"></param>
|
||||||
/// <param name="_user0"></param>
|
/// <param name="user"></param>
|
||||||
public Group(string name, User _user0)
|
public Group(string name, User user)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
|
if (string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(name));
|
||||||
|
}
|
||||||
|
|
||||||
this.Name = name;
|
this.Name = name;
|
||||||
|
user.Groups.Add(this);
|
||||||
|
|
||||||
if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
|
this.Permissions = new HashSet<Permission>();
|
||||||
_user0.Groups.Add(this);
|
|
||||||
|
|
||||||
this.GroupPermissions = new HashSet<Permission>();
|
|
||||||
this.ProviderMappings = new HashSet<ProviderMapping>();
|
this.ProviderMappings = new HashSet<ProviderMapping>();
|
||||||
this.Preferences = new HashSet<Preference>();
|
this.Preferences = new HashSet<Preference>();
|
||||||
|
|
||||||
@ -54,9 +48,9 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="name"></param>
|
/// <param name="name"></param>
|
||||||
/// <param name="_user0"></param>
|
/// <param name="_user0"></param>
|
||||||
public static Group Create(string name, User _user0)
|
public static Group Create(string name, User user)
|
||||||
{
|
{
|
||||||
return new Group(name, _user0);
|
return new Group(name, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
@ -68,8 +62,7 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[Key]
|
[Key]
|
||||||
[Required]
|
[Required]
|
||||||
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
public Guid Id { get; protected set; }
|
||||||
public int Id { get; protected set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required, Max length = 255
|
/// Required, Max length = 255
|
||||||
@ -96,13 +89,13 @@ namespace Jellyfin.Data.Entities
|
|||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
|
|
||||||
[ForeignKey("Permission_GroupPermissions_Id")]
|
[ForeignKey("Permission_GroupPermissions_Id")]
|
||||||
public virtual ICollection<Permission> GroupPermissions { get; protected set; }
|
public ICollection<Permission> Permissions { get; protected set; }
|
||||||
|
|
||||||
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
|
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
|
||||||
public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; }
|
public ICollection<ProviderMapping> ProviderMappings { get; protected set; }
|
||||||
|
|
||||||
[ForeignKey("Preference_Preferences_Id")]
|
[ForeignKey("Preference_Preferences_Id")]
|
||||||
public virtual ICollection<Preference> Preferences { get; protected set; }
|
public ICollection<Preference> Preferences { get; protected set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,10 +3,11 @@ using System.ComponentModel;
|
|||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
|
|
||||||
namespace Jellyfin.Data.Entities
|
namespace Jellyfin.Data.Entities
|
||||||
{
|
{
|
||||||
public partial class Permission
|
public partial class Permission : ISavingChanges
|
||||||
{
|
{
|
||||||
partial void Init();
|
partial void Init();
|
||||||
|
|
||||||
@ -18,33 +19,16 @@ namespace Jellyfin.Data.Entities
|
|||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
|
||||||
/// </summary>
|
|
||||||
public static Permission CreatePermissionUnsafe()
|
|
||||||
{
|
|
||||||
return new Permission();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Public constructor with required data
|
/// Public constructor with required data
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kind"></param>
|
/// <param name="kind"></param>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
/// <param name="_user0"></param>
|
/// <param name="holderId"></param>
|
||||||
/// <param name="_group1"></param>
|
public Permission(PermissionKind kind, bool value)
|
||||||
public Permission(Enums.PermissionKind kind, bool value, User _user0, Group _group1)
|
|
||||||
{
|
{
|
||||||
this.Kind = kind;
|
Kind = kind;
|
||||||
|
Value = value;
|
||||||
this.Value = value;
|
|
||||||
|
|
||||||
if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
|
|
||||||
_user0.Permissions.Add(this);
|
|
||||||
|
|
||||||
if (_group1 == null) throw new ArgumentNullException(nameof(_group1));
|
|
||||||
_group1.GroupPermissions.Add(this);
|
|
||||||
|
|
||||||
|
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
@ -54,11 +38,10 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="kind"></param>
|
/// <param name="kind"></param>
|
||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
/// <param name="_user0"></param>
|
/// <param name="holderId"></param>
|
||||||
/// <param name="_group1"></param>
|
public static Permission Create(PermissionKind kind, bool value)
|
||||||
public static Permission Create(Enums.PermissionKind kind, bool value, User _user0, Group _group1)
|
|
||||||
{
|
{
|
||||||
return new Permission(kind, value, _user0, _group1);
|
return new Permission(kind, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
@ -76,31 +59,32 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Backing field for Kind
|
/// Backing field for Kind
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Enums.PermissionKind _Kind;
|
protected PermissionKind _Kind;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When provided in a partial class, allows value of Kind to be changed before setting.
|
/// When provided in a partial class, allows value of Kind to be changed before setting.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
partial void SetKind(Enums.PermissionKind oldValue, ref Enums.PermissionKind newValue);
|
partial void SetKind(PermissionKind oldValue, ref PermissionKind newValue);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// When provided in a partial class, allows value of Kind to be changed before returning.
|
/// When provided in a partial class, allows value of Kind to be changed before returning.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
partial void GetKind(ref Enums.PermissionKind result);
|
partial void GetKind(ref PermissionKind result);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required
|
/// Required
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public Enums.PermissionKind Kind
|
public PermissionKind Kind
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
Enums.PermissionKind value = _Kind;
|
PermissionKind value = _Kind;
|
||||||
GetKind(ref value);
|
GetKind(ref value);
|
||||||
return (_Kind = value);
|
return _Kind = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
Enums.PermissionKind oldValue = _Kind;
|
PermissionKind oldValue = _Kind;
|
||||||
SetKind(oldValue, ref value);
|
SetKind(oldValue, ref value);
|
||||||
if (oldValue != value)
|
if (oldValue != value)
|
||||||
{
|
{
|
||||||
@ -117,7 +101,7 @@ namespace Jellyfin.Data.Entities
|
|||||||
public bool Value { get; set; }
|
public bool Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required, ConcurrenyToken
|
/// Required, ConcurrencyToken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ConcurrencyCheck]
|
[ConcurrencyCheck]
|
||||||
[Required]
|
[Required]
|
||||||
@ -138,7 +122,6 @@ namespace Jellyfin.Data.Entities
|
|||||||
{
|
{
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,63 +1,33 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
|
|
||||||
namespace Jellyfin.Data.Entities
|
namespace Jellyfin.Data.Entities
|
||||||
{
|
{
|
||||||
public partial class Preference
|
/// <summary>
|
||||||
|
/// An entity representing a preference attached to a user or group.
|
||||||
|
/// </summary>
|
||||||
|
public class Preference : ISavingChanges
|
||||||
{
|
{
|
||||||
partial void Init();
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Preference"/> class.
|
||||||
|
/// Public constructor with required data.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">The preference kind.</param>
|
||||||
|
/// <param name="value">The value.</param>
|
||||||
|
public Preference(PreferenceKind kind, string value)
|
||||||
|
{
|
||||||
|
Kind = kind;
|
||||||
|
Value = value ?? throw new ArgumentNullException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Preference"/> class.
|
||||||
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected Preference()
|
protected Preference()
|
||||||
{
|
{
|
||||||
Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
|
||||||
/// </summary>
|
|
||||||
public static Preference CreatePreferenceUnsafe()
|
|
||||||
{
|
|
||||||
return new Preference();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Public constructor with required data
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="kind"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="_user0"></param>
|
|
||||||
/// <param name="_group1"></param>
|
|
||||||
public Preference(Enums.PreferenceKind kind, string value, User _user0, Group _group1)
|
|
||||||
{
|
|
||||||
this.Kind = kind;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(value)) throw new ArgumentNullException(nameof(value));
|
|
||||||
this.Value = value;
|
|
||||||
|
|
||||||
if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
|
|
||||||
_user0.Preferences.Add(this);
|
|
||||||
|
|
||||||
if (_group1 == null) throw new ArgumentNullException(nameof(_group1));
|
|
||||||
_group1.Preferences.Add(this);
|
|
||||||
|
|
||||||
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Static create function (for use in LINQ queries, etc.)
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="kind"></param>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="_user0"></param>
|
|
||||||
/// <param name="_group1"></param>
|
|
||||||
public static Preference Create(Enums.PreferenceKind kind, string value, User _user0, Group _group1)
|
|
||||||
{
|
|
||||||
return new Preference(kind, value, _user0, _group1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
@ -76,7 +46,7 @@ namespace Jellyfin.Data.Entities
|
|||||||
/// Required
|
/// Required
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Required]
|
[Required]
|
||||||
public Enums.PreferenceKind Kind { get; set; }
|
public PreferenceKind Kind { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required, Max length = 65535
|
/// Required, Max length = 65535
|
||||||
@ -87,21 +57,28 @@ namespace Jellyfin.Data.Entities
|
|||||||
public string Value { get; set; }
|
public string Value { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required, ConcurrenyToken
|
/// Required, ConcurrencyToken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ConcurrencyCheck]
|
[ConcurrencyCheck]
|
||||||
[Required]
|
[Required]
|
||||||
public uint RowVersion { get; set; }
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="kind">The preference kind.</param>
|
||||||
|
/// <param name="value">The value.</param>
|
||||||
|
/// <returns>The new instance.</returns>
|
||||||
|
public static Preference Create(PreferenceKind kind, string value)
|
||||||
|
{
|
||||||
|
return new Preference(kind, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void OnSavingChanges()
|
public void OnSavingChanges()
|
||||||
{
|
{
|
||||||
RowVersion++;
|
RowVersion++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
|
||||||
* Navigation properties
|
|
||||||
*************************************************************************/
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,45 +9,23 @@ using Jellyfin.Data.Enums;
|
|||||||
|
|
||||||
namespace Jellyfin.Data.Entities
|
namespace Jellyfin.Data.Entities
|
||||||
{
|
{
|
||||||
public partial class User
|
/// <summary>
|
||||||
|
/// An entity representing a user.
|
||||||
|
/// </summary>
|
||||||
|
public partial class User : IHasPermissions, ISavingChanges
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The values being delimited here are Guids, so commas work as they do not appear in Guids.
|
/// The values being delimited here are Guids, so commas work as they do not appear in Guids.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private const char Delimiter = ',';
|
private const char Delimiter = ',';
|
||||||
|
|
||||||
partial void Init();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
/// Initializes a new instance of the <see cref="User"/> class.
|
||||||
|
/// Public constructor with required data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected User()
|
/// <param name="username">The username for the new user.</param>
|
||||||
{
|
/// <param name="authenticationProviderId">The authentication provider's Id</param>
|
||||||
Groups = new HashSet<Group>();
|
public User(string username, string authenticationProviderId)
|
||||||
Permissions = new HashSet<Permission>();
|
|
||||||
ProviderMappings = new HashSet<ProviderMapping>();
|
|
||||||
Preferences = new HashSet<Preference>();
|
|
||||||
AccessSchedules = new HashSet<AccessSchedule>();
|
|
||||||
|
|
||||||
Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Public constructor with required data
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="username"></param>
|
|
||||||
/// <param name="mustUpdatePassword"></param>
|
|
||||||
/// <param name="authenticationProviderId"></param>
|
|
||||||
/// <param name="invalidLoginAttemptCount"></param>
|
|
||||||
/// <param name="subtitleMode"></param>
|
|
||||||
/// <param name="playDefaultAudioTrack"></param>
|
|
||||||
public User(
|
|
||||||
string username,
|
|
||||||
bool mustUpdatePassword,
|
|
||||||
string authenticationProviderId,
|
|
||||||
int invalidLoginAttemptCount,
|
|
||||||
SubtitlePlaybackMode subtitleMode,
|
|
||||||
bool playDefaultAudioTrack)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(username))
|
if (string.IsNullOrEmpty(username))
|
||||||
{
|
{
|
||||||
@ -60,11 +38,7 @@ namespace Jellyfin.Data.Entities
|
|||||||
}
|
}
|
||||||
|
|
||||||
Username = username;
|
Username = username;
|
||||||
MustUpdatePassword = mustUpdatePassword;
|
|
||||||
AuthenticationProviderId = authenticationProviderId;
|
AuthenticationProviderId = authenticationProviderId;
|
||||||
InvalidLoginAttemptCount = invalidLoginAttemptCount;
|
|
||||||
SubtitleMode = subtitleMode;
|
|
||||||
PlayDefaultAudioTrack = playDefaultAudioTrack;
|
|
||||||
|
|
||||||
Groups = new HashSet<Group>();
|
Groups = new HashSet<Group>();
|
||||||
Permissions = new HashSet<Permission>();
|
Permissions = new HashSet<Permission>();
|
||||||
@ -74,6 +48,8 @@ namespace Jellyfin.Data.Entities
|
|||||||
|
|
||||||
// Set default values
|
// Set default values
|
||||||
Id = Guid.NewGuid();
|
Id = Guid.NewGuid();
|
||||||
|
InvalidLoginAttemptCount = 0;
|
||||||
|
MustUpdatePassword = false;
|
||||||
DisplayMissingEpisodes = false;
|
DisplayMissingEpisodes = false;
|
||||||
DisplayCollectionsView = false;
|
DisplayCollectionsView = false;
|
||||||
HidePlayedInLatest = true;
|
HidePlayedInLatest = true;
|
||||||
@ -81,36 +57,40 @@ namespace Jellyfin.Data.Entities
|
|||||||
RememberSubtitleSelections = true;
|
RememberSubtitleSelections = true;
|
||||||
EnableNextEpisodeAutoPlay = true;
|
EnableNextEpisodeAutoPlay = true;
|
||||||
EnableAutoLogin = false;
|
EnableAutoLogin = false;
|
||||||
|
PlayDefaultAudioTrack = true;
|
||||||
|
SubtitleMode = SubtitlePlaybackMode.Default;
|
||||||
|
|
||||||
|
AddDefaultPermissions();
|
||||||
|
AddDefaultPreferences();
|
||||||
Init();
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
/// Initializes a new instance of the <see cref="User"/> class.
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static User CreateUserUnsafe()
|
protected User()
|
||||||
{
|
{
|
||||||
return new User();
|
Groups = new HashSet<Group>();
|
||||||
|
Permissions = new HashSet<Permission>();
|
||||||
|
ProviderMappings = new HashSet<ProviderMapping>();
|
||||||
|
Preferences = new HashSet<Preference>();
|
||||||
|
AccessSchedules = new HashSet<AccessSchedule>();
|
||||||
|
|
||||||
|
AddDefaultPermissions();
|
||||||
|
AddDefaultPreferences();
|
||||||
|
Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Static create function (for use in LINQ queries, etc.)
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="username"></param>
|
/// <param name="username">The username for the created user.</param>
|
||||||
/// <param name="mustUpdatePassword"></param>
|
/// <param name="authenticationProviderId">The Id of the user's authentication provider.</param>
|
||||||
/// <param name="authenticationProviderId"></param>
|
/// <returns>The created instance.</returns>
|
||||||
/// <param name="invalidLoginAttemptCount"></param>
|
public static User Create(string username, string authenticationProviderId)
|
||||||
/// <param name="subtitleMode"></param>
|
|
||||||
/// <param name="playDefaultAudioTrack"></param>
|
|
||||||
public static User Create(
|
|
||||||
string username,
|
|
||||||
bool mustUpdatePassword,
|
|
||||||
string authenticationProviderId,
|
|
||||||
int invalidLoginAttemptCount,
|
|
||||||
SubtitlePlaybackMode subtitleMode,
|
|
||||||
bool playDefaultAudioTrack)
|
|
||||||
{
|
{
|
||||||
return new User(username, mustUpdatePassword, authenticationProviderId, invalidLoginAttemptCount, subtitleMode, playDefaultAudioTrack);
|
return new User(username, authenticationProviderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
@ -131,7 +111,6 @@ namespace Jellyfin.Data.Entities
|
|||||||
[Required]
|
[Required]
|
||||||
[MaxLength(255)]
|
[MaxLength(255)]
|
||||||
[StringLength(255)]
|
[StringLength(255)]
|
||||||
[JsonPropertyName("Name")]
|
|
||||||
public string Username { get; set; }
|
public string Username { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -199,6 +178,7 @@ namespace Jellyfin.Data.Entities
|
|||||||
public bool PlayDefaultAudioTrack { get; set; }
|
public bool PlayDefaultAudioTrack { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Gets or sets the subtitle language preference.
|
||||||
/// Max length = 255
|
/// Max length = 255
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[MaxLength(255)]
|
[MaxLength(255)]
|
||||||
@ -237,6 +217,7 @@ namespace Jellyfin.Data.Entities
|
|||||||
public int? RemoteClientBitrateLimit { get; set; }
|
public int? RemoteClientBitrateLimit { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Gets or sets the internal id.
|
||||||
/// This is a temporary stopgap for until the library db is migrated.
|
/// This is a temporary stopgap for until the library db is migrated.
|
||||||
/// This corresponds to the value of the index of this user in the library db.
|
/// This corresponds to the value of the index of this user in the library db.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -246,7 +227,8 @@ namespace Jellyfin.Data.Entities
|
|||||||
public ImageInfo ProfileImage { get; set; }
|
public ImageInfo ProfileImage { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Required, ConcurrenyToken
|
/// Gets or sets the row version.
|
||||||
|
/// Required, ConcurrenyToken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ConcurrencyCheck]
|
[ConcurrencyCheck]
|
||||||
[Required]
|
[Required]
|
||||||
@ -260,23 +242,25 @@ namespace Jellyfin.Data.Entities
|
|||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
* Navigation properties
|
* Navigation properties
|
||||||
*************************************************************************/
|
*************************************************************************/
|
||||||
[ForeignKey("Group_Groups_Id")]
|
[ForeignKey("Group_Groups_Guid")]
|
||||||
public ICollection<Group> Groups { get; protected set; }
|
public ICollection<Group> Groups { get; protected set; }
|
||||||
|
|
||||||
[ForeignKey("Permission_Permissions_Id")]
|
[ForeignKey("Permission_Permissions_Guid")]
|
||||||
public ICollection<Permission> Permissions { get; protected set; }
|
public ICollection<Permission> Permissions { get; protected set; }
|
||||||
|
|
||||||
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
|
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
|
||||||
public ICollection<ProviderMapping> ProviderMappings { get; protected set; }
|
public ICollection<ProviderMapping> ProviderMappings { get; protected set; }
|
||||||
|
|
||||||
[ForeignKey("Preference_Preferences_Id")]
|
[ForeignKey("Preference_Preferences_Guid")]
|
||||||
public ICollection<Preference> Preferences { get; protected set; }
|
public ICollection<Preference> Preferences { get; protected set; }
|
||||||
|
|
||||||
public ICollection<AccessSchedule> AccessSchedules { get; protected set; }
|
public ICollection<AccessSchedule> AccessSchedules { get; protected set; }
|
||||||
|
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
public bool HasPermission(PermissionKind permission)
|
public bool HasPermission(PermissionKind permission)
|
||||||
{
|
{
|
||||||
return Permissions.Select(p => p.Kind).Contains(permission);
|
return Permissions.First(p => p.Kind == permission).Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPermission(PermissionKind kind, bool value)
|
public void SetPermission(PermissionKind kind, bool value)
|
||||||
@ -287,11 +271,12 @@ namespace Jellyfin.Data.Entities
|
|||||||
|
|
||||||
public string[] GetPreference(PreferenceKind preference)
|
public string[] GetPreference(PreferenceKind preference)
|
||||||
{
|
{
|
||||||
return Preferences
|
var val = Preferences
|
||||||
.Where(p => p.Kind == preference)
|
.Where(p => p.Kind == preference)
|
||||||
.Select(p => p.Value)
|
.Select(p => p.Value)
|
||||||
.First()
|
.First();
|
||||||
.Split(Delimiter);
|
|
||||||
|
return Equals(val, string.Empty) ? Array.Empty<string>() : val.Split(Delimiter);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPreference(PreferenceKind preference, string[] values)
|
public void SetPreference(PreferenceKind preference, string[] values)
|
||||||
@ -332,5 +317,39 @@ namespace Jellyfin.Data.Entities
|
|||||||
|
|
||||||
return hour >= schedule.StartHour && hour <= schedule.EndHour;
|
return hour >= schedule.StartHour && hour <= schedule.EndHour;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: make these user configurable?
|
||||||
|
private void AddDefaultPermissions()
|
||||||
|
{
|
||||||
|
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.EnableAllDevices, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableAllFolders, false));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableContentDeletion, false));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableContentDownloading, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableMediaConversion, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableMediaPlayback, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnablePlaybackRemuxing, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnablePublicSharing, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableRemoteAccess, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableSyncTranscoding, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableAudioPlaybackTranscoding, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableLiveTvAccess, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableLiveTvManagement, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableSharedDeviceControl, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableVideoPlaybackTranscoding, true));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.ForceRemoteSourceTranscoding, false));
|
||||||
|
Permissions.Add(new Permission(PermissionKind.EnableRemoteControlOfOtherUsers, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddDefaultPreferences()
|
||||||
|
{
|
||||||
|
foreach (var val in Enum.GetValues(typeof(PreferenceKind)).Cast<PreferenceKind>())
|
||||||
|
{
|
||||||
|
Preferences.Add(new Preference(val, string.Empty));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
Jellyfin.Data/IHasPermissions.cs
Normal file
10
Jellyfin.Data/IHasPermissions.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Jellyfin.Data.Entities;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data
|
||||||
|
{
|
||||||
|
public interface IHasPermissions
|
||||||
|
{
|
||||||
|
ICollection<Permission> Permissions { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,13 @@ namespace Jellyfin.Server.Implementations
|
|||||||
public partial class JellyfinDb : DbContext
|
public partial class JellyfinDb : DbContext
|
||||||
{
|
{
|
||||||
public virtual DbSet<ActivityLog> ActivityLogs { get; set; }
|
public virtual DbSet<ActivityLog> ActivityLogs { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Group> Groups { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Permission> Permissions { get; set; }
|
||||||
|
|
||||||
|
public virtual DbSet<Preference> Preferences { get; set; }
|
||||||
|
|
||||||
public virtual DbSet<Data.Entities.User> Users { get; set; }
|
public virtual DbSet<Data.Entities.User> Users { get; set; }
|
||||||
/*public virtual DbSet<Artwork> Artwork { get; set; }
|
/*public virtual DbSet<Artwork> Artwork { get; set; }
|
||||||
public virtual DbSet<Book> Books { get; set; }
|
public virtual DbSet<Book> Books { get; set; }
|
||||||
@ -30,7 +37,6 @@ namespace Jellyfin.Server.Implementations
|
|||||||
public virtual DbSet<Episode> Episodes { get; set; }
|
public virtual DbSet<Episode> Episodes { get; set; }
|
||||||
public virtual DbSet<EpisodeMetadata> EpisodeMetadata { get; set; }
|
public virtual DbSet<EpisodeMetadata> EpisodeMetadata { get; set; }
|
||||||
public virtual DbSet<Genre> Genres { get; set; }
|
public virtual DbSet<Genre> Genres { get; set; }
|
||||||
public virtual DbSet<Group> Groups { get; set; }
|
|
||||||
public virtual DbSet<Library> Libraries { get; set; }
|
public virtual DbSet<Library> Libraries { get; set; }
|
||||||
public virtual DbSet<LibraryItem> LibraryItems { get; set; }
|
public virtual DbSet<LibraryItem> LibraryItems { get; set; }
|
||||||
public virtual DbSet<LibraryRoot> LibraryRoot { get; set; }
|
public virtual DbSet<LibraryRoot> LibraryRoot { get; set; }
|
||||||
@ -43,12 +49,10 @@ namespace Jellyfin.Server.Implementations
|
|||||||
public virtual DbSet<MovieMetadata> MovieMetadata { get; set; }
|
public virtual DbSet<MovieMetadata> MovieMetadata { get; set; }
|
||||||
public virtual DbSet<MusicAlbum> MusicAlbums { get; set; }
|
public virtual DbSet<MusicAlbum> MusicAlbums { get; set; }
|
||||||
public virtual DbSet<MusicAlbumMetadata> MusicAlbumMetadata { get; set; }
|
public virtual DbSet<MusicAlbumMetadata> MusicAlbumMetadata { get; set; }
|
||||||
public virtual DbSet<Permission> Permissions { get; set; }
|
|
||||||
public virtual DbSet<Person> People { get; set; }
|
public virtual DbSet<Person> People { get; set; }
|
||||||
public virtual DbSet<PersonRole> PersonRoles { get; set; }
|
public virtual DbSet<PersonRole> PersonRoles { get; set; }
|
||||||
public virtual DbSet<Photo> Photo { get; set; }
|
public virtual DbSet<Photo> Photo { get; set; }
|
||||||
public virtual DbSet<PhotoMetadata> PhotoMetadata { get; set; }
|
public virtual DbSet<PhotoMetadata> PhotoMetadata { get; set; }
|
||||||
public virtual DbSet<Preference> Preferences { get; set; }
|
|
||||||
public virtual DbSet<ProviderMapping> ProviderMappings { get; set; }
|
public virtual DbSet<ProviderMapping> ProviderMappings { get; set; }
|
||||||
public virtual DbSet<Rating> Ratings { get; set; }
|
public virtual DbSet<Rating> Ratings { get; set; }
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ namespace Jellyfin.Server.Implementations
|
|||||||
public JellyfinDbProvider(IServiceProvider serviceProvider)
|
public JellyfinDbProvider(IServiceProvider serviceProvider)
|
||||||
{
|
{
|
||||||
_serviceProvider = serviceProvider;
|
_serviceProvider = serviceProvider;
|
||||||
serviceProvider.GetService<JellyfinDb>().Database.Migrate();
|
serviceProvider.GetRequiredService<JellyfinDb>().Database.Migrate();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1601
|
#pragma warning disable SA1601
|
||||||
|
|
||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
@ -12,8 +12,8 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|||||||
namespace Jellyfin.Server.Implementations.Migrations
|
namespace Jellyfin.Server.Implementations.Migrations
|
||||||
{
|
{
|
||||||
[DbContext(typeof(JellyfinDb))]
|
[DbContext(typeof(JellyfinDb))]
|
||||||
[Migration("20200504195702_UserSchema")]
|
[Migration("20200517002411_AddUsers")]
|
||||||
partial class UserSchema
|
partial class AddUsers
|
||||||
{
|
{
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
@ -22,6 +22,31 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.HasDefaultSchema("jellyfin")
|
.HasDefaultSchema("jellyfin")
|
||||||
.HasAnnotation("ProductVersion", "3.1.3");
|
.HasAnnotation("ProductVersion", "3.1.3");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("DayOfWeek")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("EndHour")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<double>("StartHour")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AccessSchedule");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -65,17 +90,17 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("ActivityLog");
|
b.ToTable("ActivityLogs");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("Group_Groups_Id")
|
b.Property<Guid?>("Group_Groups_Guid")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -88,9 +113,27 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("Group_Groups_Id");
|
b.HasIndex("Group_Groups_Guid");
|
||||||
|
|
||||||
b.ToTable("Group");
|
b.ToTable("Groups");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModified")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ImageInfo");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
||||||
@ -102,11 +145,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
b.Property<int>("Kind")
|
b.Property<int>("Kind")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int?>("Permission_GroupPermissions_Id")
|
b.Property<Guid?>("Permission_GroupPermissions_Id")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("Permission_Permissions_Id")
|
b.Property<Guid?>("Permission_Permissions_Guid")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<uint>("RowVersion")
|
b.Property<uint>("RowVersion")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
@ -119,9 +162,9 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Permission_GroupPermissions_Id");
|
b.HasIndex("Permission_GroupPermissions_Id");
|
||||||
|
|
||||||
b.HasIndex("Permission_Permissions_Id");
|
b.HasIndex("Permission_Permissions_Guid");
|
||||||
|
|
||||||
b.ToTable("Permission");
|
b.ToTable("Permissions");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
||||||
@ -133,8 +176,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
b.Property<int>("Kind")
|
b.Property<int>("Kind")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int?>("Preference_Preferences_Id")
|
b.Property<Guid?>("Preference_Preferences_Guid")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("Preference_Preferences_Id")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<uint>("RowVersion")
|
b.Property<uint>("RowVersion")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
@ -147,9 +193,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Preference_Preferences_Guid");
|
||||||
|
|
||||||
b.HasIndex("Preference_Preferences_Id");
|
b.HasIndex("Preference_Preferences_Id");
|
||||||
|
|
||||||
b.ToTable("Preference");
|
b.ToTable("Preferences");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
|
||||||
@ -163,8 +211,8 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(65535);
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
b.Property<int?>("ProviderMapping_ProviderMappings_Id")
|
b.Property<Guid?>("ProviderMapping_ProviderMappings_Id")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("ProviderName")
|
b.Property<string>("ProviderName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -189,12 +237,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("AudioLanguagePreference")
|
b.Property<string>("AudioLanguagePreference")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(255);
|
.HasMaxLength(255);
|
||||||
|
|
||||||
@ -203,71 +250,86 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(255);
|
.HasMaxLength(255);
|
||||||
|
|
||||||
b.Property<bool?>("DisplayCollectionsView")
|
b.Property<bool>("DisplayCollectionsView")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("DisplayMissingEpisodes")
|
b.Property<bool>("DisplayMissingEpisodes")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("EnableNextEpisodeAutoPlay")
|
b.Property<string>("EasyPassword")
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<bool?>("EnableUserPreferenceAccess")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("GroupedFolders")
|
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(65535);
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
b.Property<bool?>("HidePlayedInLatest")
|
b.Property<bool>("EnableAutoLogin")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableLocalPassword")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableNextEpisodeAutoPlay")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableUserPreferenceAccess")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("HidePlayedInLatest")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("InternalId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("InvalidLoginAttemptCount")
|
b.Property<int>("InvalidLoginAttemptCount")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("LatestItemExcludes")
|
b.Property<DateTime>("LastActivityDate")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT");
|
||||||
.HasMaxLength(65535);
|
|
||||||
|
b.Property<DateTime>("LastLoginDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("LoginAttemptsBeforeLockout")
|
b.Property<int?>("LoginAttemptsBeforeLockout")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("MaxParentalAgeRating")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool>("MustUpdatePassword")
|
b.Property<bool>("MustUpdatePassword")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("MyMediaExcludes")
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasMaxLength(65535);
|
|
||||||
|
|
||||||
b.Property<string>("OrderedViews")
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasMaxLength(65535);
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
b.Property<string>("Password")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(65535);
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordResetProviderId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(255);
|
||||||
|
|
||||||
b.Property<bool>("PlayDefaultAudioTrack")
|
b.Property<bool>("PlayDefaultAudioTrack")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("RememberAudioSelections")
|
b.Property<int?>("ProfileImageId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("RememberSubtitleSelections")
|
b.Property<bool>("RememberAudioSelections")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("RememberSubtitleSelections")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("RemoteClientBitrateLimit")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<uint>("RowVersion")
|
b.Property<uint>("RowVersion")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("SubtitleLanguagePrefernce")
|
b.Property<string>("SubtitleLanguagePreference")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(255);
|
.HasMaxLength(255);
|
||||||
|
|
||||||
b.Property<string>("SubtitleMode")
|
b.Property<int>("SubtitleMode")
|
||||||
.IsRequired()
|
.HasColumnType("INTEGER");
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasMaxLength(255);
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
b.Property<string>("Username")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -276,34 +338,45 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("User");
|
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.Group", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Jellyfin.Data.Entities.User", null)
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
.WithMany("Groups")
|
.WithMany("Groups")
|
||||||
.HasForeignKey("Group_Groups_Id");
|
.HasForeignKey("Group_Groups_Guid");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
||||||
.WithMany("GroupPermissions")
|
.WithMany("Permissions")
|
||||||
.HasForeignKey("Permission_GroupPermissions_Id");
|
.HasForeignKey("Permission_GroupPermissions_Id");
|
||||||
|
|
||||||
b.HasOne("Jellyfin.Data.Entities.User", null)
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
.WithMany("Permissions")
|
.WithMany("Permissions")
|
||||||
.HasForeignKey("Permission_Permissions_Id");
|
.HasForeignKey("Permission_Permissions_Guid");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
|
||||||
.WithMany("Preferences")
|
|
||||||
.HasForeignKey("Preference_Preferences_Id");
|
|
||||||
|
|
||||||
b.HasOne("Jellyfin.Data.Entities.User", null)
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithMany("Preferences")
|
||||||
|
.HasForeignKey("Preference_Preferences_Guid");
|
||||||
|
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
||||||
.WithMany("Preferences")
|
.WithMany("Preferences")
|
||||||
.HasForeignKey("Preference_Preferences_Id");
|
.HasForeignKey("Preference_Preferences_Id");
|
||||||
});
|
});
|
||||||
@ -318,6 +391,13 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.WithMany("ProviderMappings")
|
.WithMany("ProviderMappings")
|
||||||
.HasForeignKey("ProviderMapping_ProviderMappings_Id");
|
.HasForeignKey("ProviderMapping_ProviderMappings_Id");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.ImageInfo", "ProfileImage")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProfileImageId");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,74 +1,125 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
#pragma warning disable SA1601
|
#pragma warning disable SA1601
|
||||||
|
|
||||||
|
using System;
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
using Microsoft.EntityFrameworkCore.Migrations;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.Migrations
|
namespace Jellyfin.Server.Implementations.Migrations
|
||||||
{
|
{
|
||||||
public partial class UserSchema : Migration
|
public partial class AddUsers : Migration
|
||||||
{
|
{
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
protected override void Up(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "User",
|
name: "ImageInfo",
|
||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
Id = table.Column<int>(nullable: false)
|
Id = table.Column<int>(nullable: false)
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
Path = table.Column<string>(nullable: false),
|
||||||
|
LastModified = table.Column<DateTime>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_ImageInfo", x => x.Id);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Users",
|
||||||
|
schema: "jellyfin",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(nullable: false),
|
||||||
Username = table.Column<string>(maxLength: 255, nullable: false),
|
Username = table.Column<string>(maxLength: 255, nullable: false),
|
||||||
Password = table.Column<string>(maxLength: 65535, nullable: true),
|
Password = table.Column<string>(maxLength: 65535, nullable: true),
|
||||||
|
EasyPassword = table.Column<string>(maxLength: 65535, nullable: true),
|
||||||
MustUpdatePassword = table.Column<bool>(nullable: false),
|
MustUpdatePassword = table.Column<bool>(nullable: false),
|
||||||
AudioLanguagePreference = table.Column<string>(maxLength: 255, nullable: false),
|
AudioLanguagePreference = table.Column<string>(maxLength: 255, nullable: true),
|
||||||
AuthenticationProviderId = table.Column<string>(maxLength: 255, nullable: false),
|
AuthenticationProviderId = table.Column<string>(maxLength: 255, nullable: false),
|
||||||
GroupedFolders = table.Column<string>(maxLength: 65535, nullable: true),
|
PasswordResetProviderId = table.Column<string>(maxLength: 255, nullable: false),
|
||||||
InvalidLoginAttemptCount = table.Column<int>(nullable: false),
|
InvalidLoginAttemptCount = table.Column<int>(nullable: false),
|
||||||
LatestItemExcludes = table.Column<string>(maxLength: 65535, nullable: true),
|
LastActivityDate = table.Column<DateTime>(nullable: false),
|
||||||
|
LastLoginDate = table.Column<DateTime>(nullable: false),
|
||||||
LoginAttemptsBeforeLockout = table.Column<int>(nullable: true),
|
LoginAttemptsBeforeLockout = table.Column<int>(nullable: true),
|
||||||
MyMediaExcludes = table.Column<string>(maxLength: 65535, nullable: true),
|
SubtitleMode = table.Column<int>(nullable: false),
|
||||||
OrderedViews = table.Column<string>(maxLength: 65535, nullable: true),
|
|
||||||
SubtitleMode = table.Column<string>(maxLength: 255, nullable: false),
|
|
||||||
PlayDefaultAudioTrack = table.Column<bool>(nullable: false),
|
PlayDefaultAudioTrack = table.Column<bool>(nullable: false),
|
||||||
SubtitleLanguagePrefernce = table.Column<string>(maxLength: 255, nullable: true),
|
SubtitleLanguagePreference = table.Column<string>(maxLength: 255, nullable: true),
|
||||||
DisplayMissingEpisodes = table.Column<bool>(nullable: true),
|
DisplayMissingEpisodes = table.Column<bool>(nullable: false),
|
||||||
DisplayCollectionsView = table.Column<bool>(nullable: true),
|
DisplayCollectionsView = table.Column<bool>(nullable: false),
|
||||||
HidePlayedInLatest = table.Column<bool>(nullable: true),
|
EnableLocalPassword = table.Column<bool>(nullable: false),
|
||||||
RememberAudioSelections = table.Column<bool>(nullable: true),
|
HidePlayedInLatest = table.Column<bool>(nullable: false),
|
||||||
RememberSubtitleSelections = table.Column<bool>(nullable: true),
|
RememberAudioSelections = table.Column<bool>(nullable: false),
|
||||||
EnableNextEpisodeAutoPlay = table.Column<bool>(nullable: true),
|
RememberSubtitleSelections = table.Column<bool>(nullable: false),
|
||||||
EnableUserPreferenceAccess = table.Column<bool>(nullable: true),
|
EnableNextEpisodeAutoPlay = table.Column<bool>(nullable: false),
|
||||||
|
EnableAutoLogin = table.Column<bool>(nullable: false),
|
||||||
|
EnableUserPreferenceAccess = table.Column<bool>(nullable: false),
|
||||||
|
MaxParentalAgeRating = table.Column<int>(nullable: true),
|
||||||
|
RemoteClientBitrateLimit = table.Column<int>(nullable: true),
|
||||||
|
InternalId = table.Column<long>(nullable: false),
|
||||||
|
ProfileImageId = table.Column<int>(nullable: true),
|
||||||
RowVersion = table.Column<uint>(nullable: false)
|
RowVersion = table.Column<uint>(nullable: false)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_User", x => x.Id);
|
table.PrimaryKey("PK_Users", x => x.Id);
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Group",
|
|
||||||
schema: "jellyfin",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(nullable: false)
|
|
||||||
.Annotation("Sqlite:Autoincrement", true),
|
|
||||||
Name = table.Column<string>(maxLength: 255, nullable: false),
|
|
||||||
RowVersion = table.Column<uint>(nullable: false),
|
|
||||||
Group_Groups_Id = table.Column<int>(nullable: true)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Group", x => x.Id);
|
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Group_User_Group_Groups_Id",
|
name: "FK_Users_ImageInfo_ProfileImageId",
|
||||||
column: x => x.Group_Groups_Id,
|
column: x => x.ProfileImageId,
|
||||||
principalSchema: "jellyfin",
|
principalSchema: "jellyfin",
|
||||||
principalTable: "User",
|
principalTable: "ImageInfo",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Permission",
|
name: "AccessSchedule",
|
||||||
|
schema: "jellyfin",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<int>(nullable: false)
|
||||||
|
.Annotation("Sqlite:Autoincrement", true),
|
||||||
|
UserId = table.Column<Guid>(nullable: false),
|
||||||
|
DayOfWeek = table.Column<int>(nullable: false),
|
||||||
|
StartHour = table.Column<double>(nullable: false),
|
||||||
|
EndHour = table.Column<double>(nullable: false)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_AccessSchedule", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_AccessSchedule_Users_UserId",
|
||||||
|
column: x => x.UserId,
|
||||||
|
principalSchema: "jellyfin",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Cascade);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Groups",
|
||||||
|
schema: "jellyfin",
|
||||||
|
columns: table => new
|
||||||
|
{
|
||||||
|
Id = table.Column<Guid>(nullable: false),
|
||||||
|
Name = table.Column<string>(maxLength: 255, nullable: false),
|
||||||
|
RowVersion = table.Column<uint>(nullable: false),
|
||||||
|
Group_Groups_Guid = table.Column<Guid>(nullable: true)
|
||||||
|
},
|
||||||
|
constraints: table =>
|
||||||
|
{
|
||||||
|
table.PrimaryKey("PK_Groups", x => x.Id);
|
||||||
|
table.ForeignKey(
|
||||||
|
name: "FK_Groups_Users_Group_Groups_Guid",
|
||||||
|
column: x => x.Group_Groups_Guid,
|
||||||
|
principalSchema: "jellyfin",
|
||||||
|
principalTable: "Users",
|
||||||
|
principalColumn: "Id",
|
||||||
|
onDelete: ReferentialAction.Restrict);
|
||||||
|
});
|
||||||
|
|
||||||
|
migrationBuilder.CreateTable(
|
||||||
|
name: "Permissions",
|
||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
@ -77,30 +128,30 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
Kind = table.Column<int>(nullable: false),
|
Kind = table.Column<int>(nullable: false),
|
||||||
Value = table.Column<bool>(nullable: false),
|
Value = table.Column<bool>(nullable: false),
|
||||||
RowVersion = table.Column<uint>(nullable: false),
|
RowVersion = table.Column<uint>(nullable: false),
|
||||||
Permission_GroupPermissions_Id = table.Column<int>(nullable: true),
|
Permission_GroupPermissions_Id = table.Column<Guid>(nullable: true),
|
||||||
Permission_Permissions_Id = table.Column<int>(nullable: true)
|
Permission_Permissions_Guid = table.Column<Guid>(nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Permission", x => x.Id);
|
table.PrimaryKey("PK_Permissions", x => x.Id);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Permission_Group_Permission_GroupPermissions_Id",
|
name: "FK_Permissions_Groups_Permission_GroupPermissions_Id",
|
||||||
column: x => x.Permission_GroupPermissions_Id,
|
column: x => x.Permission_GroupPermissions_Id,
|
||||||
principalSchema: "jellyfin",
|
principalSchema: "jellyfin",
|
||||||
principalTable: "Group",
|
principalTable: "Groups",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Permission_User_Permission_Permissions_Id",
|
name: "FK_Permissions_Users_Permission_Permissions_Guid",
|
||||||
column: x => x.Permission_Permissions_Id,
|
column: x => x.Permission_Permissions_Guid,
|
||||||
principalSchema: "jellyfin",
|
principalSchema: "jellyfin",
|
||||||
principalTable: "User",
|
principalTable: "Users",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
migrationBuilder.CreateTable(
|
||||||
name: "Preference",
|
name: "Preferences",
|
||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
columns: table => new
|
columns: table => new
|
||||||
{
|
{
|
||||||
@ -109,23 +160,24 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
Kind = table.Column<int>(nullable: false),
|
Kind = table.Column<int>(nullable: false),
|
||||||
Value = table.Column<string>(maxLength: 65535, nullable: false),
|
Value = table.Column<string>(maxLength: 65535, nullable: false),
|
||||||
RowVersion = table.Column<uint>(nullable: false),
|
RowVersion = table.Column<uint>(nullable: false),
|
||||||
Preference_Preferences_Id = table.Column<int>(nullable: true)
|
Preference_Preferences_Guid = table.Column<Guid>(nullable: true),
|
||||||
|
Preference_Preferences_Id = table.Column<Guid>(nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_Preference", x => x.Id);
|
table.PrimaryKey("PK_Preferences", x => x.Id);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Preference_Group_Preference_Preferences_Id",
|
name: "FK_Preferences_Users_Preference_Preferences_Guid",
|
||||||
column: x => x.Preference_Preferences_Id,
|
column: x => x.Preference_Preferences_Guid,
|
||||||
principalSchema: "jellyfin",
|
principalSchema: "jellyfin",
|
||||||
principalTable: "Group",
|
principalTable: "Users",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_Preference_User_Preference_Preferences_Id",
|
name: "FK_Preferences_Groups_Preference_Preferences_Id",
|
||||||
column: x => x.Preference_Preferences_Id,
|
column: x => x.Preference_Preferences_Id,
|
||||||
principalSchema: "jellyfin",
|
principalSchema: "jellyfin",
|
||||||
principalTable: "User",
|
principalTable: "Groups",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
});
|
});
|
||||||
@ -141,49 +193,61 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
ProviderSecrets = table.Column<string>(maxLength: 65535, nullable: false),
|
ProviderSecrets = table.Column<string>(maxLength: 65535, nullable: false),
|
||||||
ProviderData = table.Column<string>(maxLength: 65535, nullable: false),
|
ProviderData = table.Column<string>(maxLength: 65535, nullable: false),
|
||||||
RowVersion = table.Column<uint>(nullable: false),
|
RowVersion = table.Column<uint>(nullable: false),
|
||||||
ProviderMapping_ProviderMappings_Id = table.Column<int>(nullable: true)
|
ProviderMapping_ProviderMappings_Id = table.Column<Guid>(nullable: true)
|
||||||
},
|
},
|
||||||
constraints: table =>
|
constraints: table =>
|
||||||
{
|
{
|
||||||
table.PrimaryKey("PK_ProviderMapping", x => x.Id);
|
table.PrimaryKey("PK_ProviderMapping", x => x.Id);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_ProviderMapping_Group_ProviderMapping_ProviderMappings_Id",
|
name: "FK_ProviderMapping_Groups_ProviderMapping_ProviderMappings_Id",
|
||||||
column: x => x.ProviderMapping_ProviderMappings_Id,
|
column: x => x.ProviderMapping_ProviderMappings_Id,
|
||||||
principalSchema: "jellyfin",
|
principalSchema: "jellyfin",
|
||||||
principalTable: "Group",
|
principalTable: "Groups",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
table.ForeignKey(
|
table.ForeignKey(
|
||||||
name: "FK_ProviderMapping_User_ProviderMapping_ProviderMappings_Id",
|
name: "FK_ProviderMapping_Users_ProviderMapping_ProviderMappings_Id",
|
||||||
column: x => x.ProviderMapping_ProviderMappings_Id,
|
column: x => x.ProviderMapping_ProviderMappings_Id,
|
||||||
principalSchema: "jellyfin",
|
principalSchema: "jellyfin",
|
||||||
principalTable: "User",
|
principalTable: "Users",
|
||||||
principalColumn: "Id",
|
principalColumn: "Id",
|
||||||
onDelete: ReferentialAction.Restrict);
|
onDelete: ReferentialAction.Restrict);
|
||||||
});
|
});
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Group_Group_Groups_Id",
|
name: "IX_AccessSchedule_UserId",
|
||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
table: "Group",
|
table: "AccessSchedule",
|
||||||
column: "Group_Groups_Id");
|
column: "UserId");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Permission_Permission_GroupPermissions_Id",
|
name: "IX_Groups_Group_Groups_Guid",
|
||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
table: "Permission",
|
table: "Groups",
|
||||||
|
column: "Group_Groups_Guid");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Permissions_Permission_GroupPermissions_Id",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "Permissions",
|
||||||
column: "Permission_GroupPermissions_Id");
|
column: "Permission_GroupPermissions_Id");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Permission_Permission_Permissions_Id",
|
name: "IX_Permissions_Permission_Permissions_Guid",
|
||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
table: "Permission",
|
table: "Permissions",
|
||||||
column: "Permission_Permissions_Id");
|
column: "Permission_Permissions_Guid");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
name: "IX_Preference_Preference_Preferences_Id",
|
name: "IX_Preferences_Preference_Preferences_Guid",
|
||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
table: "Preference",
|
table: "Preferences",
|
||||||
|
column: "Preference_Preferences_Guid");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Preferences_Preference_Preferences_Id",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "Preferences",
|
||||||
column: "Preference_Preferences_Id");
|
column: "Preference_Preferences_Id");
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
migrationBuilder.CreateIndex(
|
||||||
@ -191,16 +255,26 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
schema: "jellyfin",
|
schema: "jellyfin",
|
||||||
table: "ProviderMapping",
|
table: "ProviderMapping",
|
||||||
column: "ProviderMapping_ProviderMappings_Id");
|
column: "ProviderMapping_ProviderMappings_Id");
|
||||||
|
|
||||||
|
migrationBuilder.CreateIndex(
|
||||||
|
name: "IX_Users_ProfileImageId",
|
||||||
|
schema: "jellyfin",
|
||||||
|
table: "Users",
|
||||||
|
column: "ProfileImageId");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
protected override void Down(MigrationBuilder migrationBuilder)
|
||||||
{
|
{
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Permission",
|
name: "AccessSchedule",
|
||||||
schema: "jellyfin");
|
schema: "jellyfin");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Preference",
|
name: "Permissions",
|
||||||
|
schema: "jellyfin");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "Preferences",
|
||||||
schema: "jellyfin");
|
schema: "jellyfin");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
@ -208,11 +282,15 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
schema: "jellyfin");
|
schema: "jellyfin");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "Group",
|
name: "Groups",
|
||||||
schema: "jellyfin");
|
schema: "jellyfin");
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
migrationBuilder.DropTable(
|
||||||
name: "User",
|
name: "Users",
|
||||||
|
schema: "jellyfin");
|
||||||
|
|
||||||
|
migrationBuilder.DropTable(
|
||||||
|
name: "ImageInfo",
|
||||||
schema: "jellyfin");
|
schema: "jellyfin");
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,7 +1,9 @@
|
|||||||
// <auto-generated />
|
// <auto-generated />
|
||||||
using System;
|
using System;
|
||||||
|
using Jellyfin.Server.Implementations;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.Migrations
|
namespace Jellyfin.Server.Implementations.Migrations
|
||||||
{
|
{
|
||||||
@ -15,6 +17,31 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.HasDefaultSchema("jellyfin")
|
.HasDefaultSchema("jellyfin")
|
||||||
.HasAnnotation("ProductVersion", "3.1.3");
|
.HasAnnotation("ProductVersion", "3.1.3");
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int>("DayOfWeek")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<double>("EndHour")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<double>("StartHour")
|
||||||
|
.HasColumnType("REAL");
|
||||||
|
|
||||||
|
b.Property<Guid>("UserId")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("UserId");
|
||||||
|
|
||||||
|
b.ToTable("AccessSchedule");
|
||||||
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<int>("Id")
|
||||||
@ -63,12 +90,12 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("Group_Groups_Id")
|
b.Property<Guid?>("Group_Groups_Guid")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("Name")
|
b.Property<string>("Name")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -81,9 +108,27 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.HasIndex("Group_Groups_Id");
|
b.HasIndex("Group_Groups_Guid");
|
||||||
|
|
||||||
b.ToTable("Group");
|
b.ToTable("Groups");
|
||||||
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
|
||||||
|
{
|
||||||
|
b.Property<int>("Id")
|
||||||
|
.ValueGeneratedOnAdd()
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<DateTime>("LastModified")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<string>("Path")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.ToTable("ImageInfo");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
||||||
@ -95,11 +140,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
b.Property<int>("Kind")
|
b.Property<int>("Kind")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int?>("Permission_GroupPermissions_Id")
|
b.Property<Guid?>("Permission_GroupPermissions_Id")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("Permission_Permissions_Id")
|
b.Property<Guid?>("Permission_Permissions_Guid")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<uint>("RowVersion")
|
b.Property<uint>("RowVersion")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
@ -112,9 +157,9 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasIndex("Permission_GroupPermissions_Id");
|
b.HasIndex("Permission_GroupPermissions_Id");
|
||||||
|
|
||||||
b.HasIndex("Permission_Permissions_Id");
|
b.HasIndex("Permission_Permissions_Guid");
|
||||||
|
|
||||||
b.ToTable("Permission");
|
b.ToTable("Permissions");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
||||||
@ -126,8 +171,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
b.Property<int>("Kind")
|
b.Property<int>("Kind")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int?>("Preference_Preferences_Id")
|
b.Property<Guid?>("Preference_Preferences_Guid")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
|
b.Property<Guid?>("Preference_Preferences_Id")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<uint>("RowVersion")
|
b.Property<uint>("RowVersion")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
@ -140,9 +188,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
|
b.HasIndex("Preference_Preferences_Guid");
|
||||||
|
|
||||||
b.HasIndex("Preference_Preferences_Id");
|
b.HasIndex("Preference_Preferences_Id");
|
||||||
|
|
||||||
b.ToTable("Preference");
|
b.ToTable("Preferences");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.ProviderMapping", b =>
|
||||||
@ -156,8 +206,8 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(65535);
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
b.Property<int?>("ProviderMapping_ProviderMappings_Id")
|
b.Property<Guid?>("ProviderMapping_ProviderMappings_Id")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("ProviderName")
|
b.Property<string>("ProviderName")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -182,12 +232,11 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
|
||||||
{
|
{
|
||||||
b.Property<int>("Id")
|
b.Property<Guid>("Id")
|
||||||
.ValueGeneratedOnAdd()
|
.ValueGeneratedOnAdd()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<string>("AudioLanguagePreference")
|
b.Property<string>("AudioLanguagePreference")
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(255);
|
.HasMaxLength(255);
|
||||||
|
|
||||||
@ -196,71 +245,86 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(255);
|
.HasMaxLength(255);
|
||||||
|
|
||||||
b.Property<bool?>("DisplayCollectionsView")
|
b.Property<bool>("DisplayCollectionsView")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("DisplayMissingEpisodes")
|
b.Property<bool>("DisplayMissingEpisodes")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("EnableNextEpisodeAutoPlay")
|
b.Property<string>("EasyPassword")
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<bool?>("EnableUserPreferenceAccess")
|
|
||||||
.HasColumnType("INTEGER");
|
|
||||||
|
|
||||||
b.Property<string>("GroupedFolders")
|
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(65535);
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
b.Property<bool?>("HidePlayedInLatest")
|
b.Property<bool>("EnableAutoLogin")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableLocalPassword")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableNextEpisodeAutoPlay")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("EnableUserPreferenceAccess")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("HidePlayedInLatest")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<long>("InternalId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<int>("InvalidLoginAttemptCount")
|
b.Property<int>("InvalidLoginAttemptCount")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("LatestItemExcludes")
|
b.Property<DateTime>("LastActivityDate")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT");
|
||||||
.HasMaxLength(65535);
|
|
||||||
|
b.Property<DateTime>("LastLoginDate")
|
||||||
|
.HasColumnType("TEXT");
|
||||||
|
|
||||||
b.Property<int?>("LoginAttemptsBeforeLockout")
|
b.Property<int?>("LoginAttemptsBeforeLockout")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("MaxParentalAgeRating")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool>("MustUpdatePassword")
|
b.Property<bool>("MustUpdatePassword")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("MyMediaExcludes")
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasMaxLength(65535);
|
|
||||||
|
|
||||||
b.Property<string>("OrderedViews")
|
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasMaxLength(65535);
|
|
||||||
|
|
||||||
b.Property<string>("Password")
|
b.Property<string>("Password")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(65535);
|
.HasMaxLength(65535);
|
||||||
|
|
||||||
|
b.Property<string>("PasswordResetProviderId")
|
||||||
|
.IsRequired()
|
||||||
|
.HasColumnType("TEXT")
|
||||||
|
.HasMaxLength(255);
|
||||||
|
|
||||||
b.Property<bool>("PlayDefaultAudioTrack")
|
b.Property<bool>("PlayDefaultAudioTrack")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("RememberAudioSelections")
|
b.Property<int?>("ProfileImageId")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<bool?>("RememberSubtitleSelections")
|
b.Property<bool>("RememberAudioSelections")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<bool>("RememberSubtitleSelections")
|
||||||
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
|
b.Property<int?>("RemoteClientBitrateLimit")
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<uint>("RowVersion")
|
b.Property<uint>("RowVersion")
|
||||||
.IsConcurrencyToken()
|
.IsConcurrencyToken()
|
||||||
.HasColumnType("INTEGER");
|
.HasColumnType("INTEGER");
|
||||||
|
|
||||||
b.Property<string>("SubtitleLanguagePrefernce")
|
b.Property<string>("SubtitleLanguagePreference")
|
||||||
.HasColumnType("TEXT")
|
.HasColumnType("TEXT")
|
||||||
.HasMaxLength(255);
|
.HasMaxLength(255);
|
||||||
|
|
||||||
b.Property<string>("SubtitleMode")
|
b.Property<int>("SubtitleMode")
|
||||||
.IsRequired()
|
.HasColumnType("INTEGER");
|
||||||
.HasColumnType("TEXT")
|
|
||||||
.HasMaxLength(255);
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
b.Property<string>("Username")
|
||||||
.IsRequired()
|
.IsRequired()
|
||||||
@ -269,34 +333,45 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
|
|
||||||
b.HasKey("Id");
|
b.HasKey("Id");
|
||||||
|
|
||||||
b.ToTable("User");
|
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.Group", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Group", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Jellyfin.Data.Entities.User", null)
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
.WithMany("Groups")
|
.WithMany("Groups")
|
||||||
.HasForeignKey("Group_Groups_Id");
|
.HasForeignKey("Group_Groups_Guid");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
||||||
.WithMany("GroupPermissions")
|
.WithMany("Permissions")
|
||||||
.HasForeignKey("Permission_GroupPermissions_Id");
|
.HasForeignKey("Permission_GroupPermissions_Id");
|
||||||
|
|
||||||
b.HasOne("Jellyfin.Data.Entities.User", null)
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
.WithMany("Permissions")
|
.WithMany("Permissions")
|
||||||
.HasForeignKey("Permission_Permissions_Id");
|
.HasForeignKey("Permission_Permissions_Guid");
|
||||||
});
|
});
|
||||||
|
|
||||||
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
|
||||||
{
|
{
|
||||||
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
|
||||||
.WithMany("Preferences")
|
|
||||||
.HasForeignKey("Preference_Preferences_Id");
|
|
||||||
|
|
||||||
b.HasOne("Jellyfin.Data.Entities.User", null)
|
b.HasOne("Jellyfin.Data.Entities.User", null)
|
||||||
|
.WithMany("Preferences")
|
||||||
|
.HasForeignKey("Preference_Preferences_Guid");
|
||||||
|
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.Group", null)
|
||||||
.WithMany("Preferences")
|
.WithMany("Preferences")
|
||||||
.HasForeignKey("Preference_Preferences_Id");
|
.HasForeignKey("Preference_Preferences_Id");
|
||||||
});
|
});
|
||||||
@ -311,6 +386,13 @@ namespace Jellyfin.Server.Implementations.Migrations
|
|||||||
.WithMany("ProviderMappings")
|
.WithMany("ProviderMappings")
|
||||||
.HasForeignKey("ProviderMapping_ProviderMappings_Id");
|
.HasForeignKey("ProviderMapping_ProviderMappings_Id");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
|
||||||
|
{
|
||||||
|
b.HasOne("Jellyfin.Data.Entities.ImageInfo", "ProfileImage")
|
||||||
|
.WithMany()
|
||||||
|
.HasForeignKey("ProfileImageId");
|
||||||
|
});
|
||||||
#pragma warning restore 612, 618
|
#pragma warning restore 612, 618
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ using MediaBrowser.Common.Cryptography;
|
|||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
using MediaBrowser.Model.Cryptography;
|
using MediaBrowser.Model.Cryptography;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.User
|
namespace Jellyfin.Server.Implementations.Users
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default authentication provider.
|
/// The default authentication provider.
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Data.Entities;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
@ -10,7 +11,7 @@ using MediaBrowser.Controller.Library;
|
|||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.User
|
namespace Jellyfin.Server.Implementations.Users
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default password reset provider.
|
/// The default password reset provider.
|
||||||
@ -94,7 +95,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(Jellyfin.Data.Entities.User user, bool isInNetwork)
|
public async Task<ForgotPasswordResult> StartForgotPasswordProcess(User user, bool isInNetwork)
|
||||||
{
|
{
|
||||||
string pin;
|
string pin;
|
||||||
using (var cryptoRandom = RandomNumberGenerator.Create())
|
using (var cryptoRandom = RandomNumberGenerator.Create())
|
@ -1,6 +1,7 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
@ -9,7 +10,7 @@ using MediaBrowser.Controller.Security;
|
|||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.User
|
namespace Jellyfin.Server.Implementations.Users
|
||||||
{
|
{
|
||||||
public sealed class DeviceAccessEntryPoint : IServerEntryPoint
|
public sealed class DeviceAccessEntryPoint : IServerEntryPoint
|
||||||
{
|
{
|
||||||
@ -33,7 +34,11 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUserUpdated(object sender, GenericEventArgs<Data.Entities.User> e)
|
public void Dispose()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUserUpdated(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
var user = e.Argument;
|
var user = e.Argument;
|
||||||
if (!user.HasPermission(PermissionKind.EnableAllDevices))
|
if (!user.HasPermission(PermissionKind.EnableAllDevices))
|
||||||
@ -42,11 +47,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
private void UpdateDeviceAccess(User user)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateDeviceAccess(Data.Entities.User user)
|
|
||||||
{
|
{
|
||||||
var existing = _authRepo.Get(new AuthenticationInfoQuery
|
var existing = _authRepo.Get(new AuthenticationInfoQuery
|
||||||
{
|
{
|
@ -1,7 +1,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Authentication;
|
using MediaBrowser.Controller.Authentication;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.User
|
namespace Jellyfin.Server.Implementations.Users
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An invalid authentication provider.
|
/// An invalid authentication provider.
|
@ -1,4 +1,4 @@
|
|||||||
#pragma warning disable CS0067
|
#pragma warning disable CA1307
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -6,7 +6,9 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Common.Cryptography;
|
using MediaBrowser.Common.Cryptography;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
@ -20,7 +22,7 @@ using MediaBrowser.Model.Events;
|
|||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Implementations.User
|
namespace Jellyfin.Server.Implementations.Users
|
||||||
{
|
{
|
||||||
public class UserManager : IUserManager
|
public class UserManager : IUserManager
|
||||||
{
|
{
|
||||||
@ -47,24 +49,24 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserPasswordChanged;
|
public event EventHandler<GenericEventArgs<User>> OnUserPasswordChanged;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserUpdated;
|
public event EventHandler<GenericEventArgs<User>> OnUserUpdated;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserCreated;
|
public event EventHandler<GenericEventArgs<User>> OnUserCreated;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserDeleted;
|
public event EventHandler<GenericEventArgs<User>> OnUserDeleted;
|
||||||
|
|
||||||
public event EventHandler<GenericEventArgs<Data.Entities.User>> OnUserLockedOut;
|
public event EventHandler<GenericEventArgs<User>> OnUserLockedOut;
|
||||||
|
|
||||||
public IEnumerable<Data.Entities.User> Users
|
public IEnumerable<User> Users
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
using var dbContext = _dbProvider.CreateContext();
|
var dbContext = _dbProvider.CreateContext();
|
||||||
return dbContext.Users;
|
return dbContext.Users;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,37 +75,38 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
using var dbContext = _dbProvider.CreateContext();
|
var dbContext = _dbProvider.CreateContext();
|
||||||
return dbContext.Users.Select(u => u.Id);
|
return dbContext.Users.Select(u => u.Id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Data.Entities.User GetUserById(Guid id)
|
public User GetUserById(Guid id)
|
||||||
{
|
{
|
||||||
if (id == Guid.Empty)
|
if (id == Guid.Empty)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Guid can't be empty", nameof(id));
|
throw new ArgumentException("Guid can't be empty", nameof(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var dbContext = _dbProvider.CreateContext();
|
var dbContext = _dbProvider.CreateContext();
|
||||||
|
|
||||||
return dbContext.Users.Find(id);
|
return dbContext.Users.Find(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Data.Entities.User GetUserByName(string name)
|
public User GetUserByName(string name)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Invalid username", nameof(name));
|
throw new ArgumentException("Invalid username", nameof(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var dbContext = _dbProvider.CreateContext();
|
var dbContext = _dbProvider.CreateContext();
|
||||||
|
|
||||||
return dbContext.Users.FirstOrDefault(u =>
|
// This can't use an overload with StringComparer because that would cause the query to
|
||||||
string.Equals(u.Username, name, StringComparison.OrdinalIgnoreCase));
|
// have to be evaluated client-side.
|
||||||
|
return dbContext.Users.FirstOrDefault(u => string.Equals(u.Username, name));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task RenameUser(Data.Entities.User user, string newName)
|
public async Task RenameUser(User user, string newName)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
@ -132,43 +135,50 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
user.Username = newName;
|
user.Username = newName;
|
||||||
await UpdateUserAsync(user).ConfigureAwait(false);
|
await UpdateUserAsync(user).ConfigureAwait(false);
|
||||||
|
|
||||||
OnUserUpdated?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
|
OnUserUpdated?.Invoke(this, new GenericEventArgs<User>(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateUser(Data.Entities.User user)
|
public void UpdateUser(User user)
|
||||||
{
|
{
|
||||||
using var dbContext = _dbProvider.CreateContext();
|
var dbContext = _dbProvider.CreateContext();
|
||||||
dbContext.Users.Update(user);
|
dbContext.Users.Update(user);
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateUserAsync(Data.Entities.User user)
|
public async Task UpdateUserAsync(User user)
|
||||||
{
|
{
|
||||||
await using var dbContext = _dbProvider.CreateContext();
|
var dbContext = _dbProvider.CreateContext();
|
||||||
dbContext.Users.Update(user);
|
dbContext.Users.Update(user);
|
||||||
|
|
||||||
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
await dbContext.SaveChangesAsync().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Data.Entities.User CreateUser(string name)
|
public User CreateUser(string name)
|
||||||
{
|
{
|
||||||
using var dbContext = _dbProvider.CreateContext();
|
if (!IsValidUsername(name))
|
||||||
|
{
|
||||||
|
throw new ArgumentException("Usernames can contain unicode symbols, numbers (0-9), dashes (-), underscores (_), apostrophes ('), and periods (.)");
|
||||||
|
}
|
||||||
|
|
||||||
var newUser = CreateUserObject(name);
|
var dbContext = _dbProvider.CreateContext();
|
||||||
|
|
||||||
|
var newUser = new User(name, _defaultAuthenticationProvider.GetType().FullName);
|
||||||
dbContext.Users.Add(newUser);
|
dbContext.Users.Add(newUser);
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
|
|
||||||
|
OnUserCreated?.Invoke(this, new GenericEventArgs<User>(newUser));
|
||||||
|
|
||||||
return newUser;
|
return newUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DeleteUser(Data.Entities.User user)
|
public void DeleteUser(User user)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(user));
|
throw new ArgumentNullException(nameof(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
using var dbContext = _dbProvider.CreateContext();
|
var dbContext = _dbProvider.CreateContext();
|
||||||
|
|
||||||
if (!dbContext.Users.Contains(user))
|
if (!dbContext.Users.Contains(user))
|
||||||
{
|
{
|
||||||
@ -200,19 +210,20 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
|
|
||||||
dbContext.Users.Remove(user);
|
dbContext.Users.Remove(user);
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
|
OnUserDeleted?.Invoke(this, new GenericEventArgs<User>(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ResetPassword(Data.Entities.User user)
|
public Task ResetPassword(User user)
|
||||||
{
|
{
|
||||||
return ChangePassword(user, string.Empty);
|
return ChangePassword(user, string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ResetEasyPassword(Data.Entities.User user)
|
public void ResetEasyPassword(User user)
|
||||||
{
|
{
|
||||||
ChangeEasyPassword(user, string.Empty, null);
|
ChangeEasyPassword(user, string.Empty, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ChangePassword(Data.Entities.User user, string newPassword)
|
public async Task ChangePassword(User user, string newPassword)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
@ -222,24 +233,18 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
|
await GetAuthenticationProvider(user).ChangePassword(user, newPassword).ConfigureAwait(false);
|
||||||
await UpdateUserAsync(user).ConfigureAwait(false);
|
await UpdateUserAsync(user).ConfigureAwait(false);
|
||||||
|
|
||||||
OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
|
OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeEasyPassword(Data.Entities.User user, string newPassword, string newPasswordSha1)
|
public void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1)
|
||||||
{
|
{
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(user));
|
|
||||||
}
|
|
||||||
|
|
||||||
GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1);
|
GetAuthenticationProvider(user).ChangeEasyPassword(user, newPassword, newPasswordSha1);
|
||||||
|
|
||||||
UpdateUser(user);
|
UpdateUser(user);
|
||||||
|
|
||||||
OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
|
OnUserPasswordChanged?.Invoke(this, new GenericEventArgs<User>(user));
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserDto GetUserDto(Data.Entities.User user, string remoteEndPoint = null)
|
public UserDto GetUserDto(User user, string remoteEndPoint = null)
|
||||||
{
|
{
|
||||||
return new UserDto
|
return new UserDto
|
||||||
{
|
{
|
||||||
@ -271,7 +276,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
MaxParentalRating = user.MaxParentalAgeRating,
|
MaxParentalRating = user.MaxParentalAgeRating,
|
||||||
EnableUserPreferenceAccess = user.EnableUserPreferenceAccess,
|
EnableUserPreferenceAccess = user.EnableUserPreferenceAccess,
|
||||||
RemoteClientBitrateLimit = user.RemoteClientBitrateLimit.GetValueOrDefault(),
|
RemoteClientBitrateLimit = user.RemoteClientBitrateLimit.GetValueOrDefault(),
|
||||||
AuthenticatioIsnProviderId = user.AuthenticationProviderId,
|
AuthenticationProviderId = user.AuthenticationProviderId,
|
||||||
PasswordResetProviderId = user.PasswordResetProviderId,
|
PasswordResetProviderId = user.PasswordResetProviderId,
|
||||||
InvalidLoginAttemptCount = user.InvalidLoginAttemptCount,
|
InvalidLoginAttemptCount = user.InvalidLoginAttemptCount,
|
||||||
LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout.GetValueOrDefault(),
|
LoginAttemptsBeforeLockout = user.LoginAttemptsBeforeLockout.GetValueOrDefault(),
|
||||||
@ -306,7 +311,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicUserDto GetPublicUserDto(Data.Entities.User user, string remoteEndPoint = null)
|
public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null)
|
||||||
{
|
{
|
||||||
if (user == null)
|
if (user == null)
|
||||||
{
|
{
|
||||||
@ -328,7 +333,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Data.Entities.User> AuthenticateUser(
|
public async Task<User> AuthenticateUser(
|
||||||
string username,
|
string username,
|
||||||
string password,
|
string password,
|
||||||
string passwordSha1,
|
string passwordSha1,
|
||||||
@ -341,7 +346,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
throw new ArgumentNullException(nameof(username));
|
throw new ArgumentNullException(nameof(username));
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = Users.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
|
var user = Users.ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
|
||||||
bool success;
|
bool success;
|
||||||
IAuthenticationProvider authenticationProvider;
|
IAuthenticationProvider authenticationProvider;
|
||||||
|
|
||||||
@ -370,7 +375,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
// Search the database for the user again
|
// Search the database for the user again
|
||||||
// the authentication provider might have created it
|
// the authentication provider might have created it
|
||||||
user = Users
|
user = Users
|
||||||
.FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
|
.ToList().FirstOrDefault(i => string.Equals(username, i.Username, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
|
if (authenticationProvider is IHasNewUserPolicy hasNewUserPolicy)
|
||||||
{
|
{
|
||||||
@ -436,10 +441,10 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
if (isUserSession)
|
if (isUserSession)
|
||||||
{
|
{
|
||||||
user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
|
user.LastActivityDate = user.LastLoginDate = DateTime.UtcNow;
|
||||||
UpdateUser(user);
|
await UpdateUserAsync(user).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
ResetInvalidLoginAttemptCount(user);
|
user.InvalidLoginAttemptCount = 0;
|
||||||
_logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Username);
|
_logger.LogInformation("Authentication request for {UserName} has succeeded.", user.Username);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -495,14 +500,11 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
public void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders)
|
public void AddParts(IEnumerable<IAuthenticationProvider> authenticationProviders, IEnumerable<IPasswordResetProvider> passwordResetProviders)
|
||||||
{
|
{
|
||||||
_authenticationProviders = authenticationProviders.ToArray();
|
_authenticationProviders = authenticationProviders.ToArray();
|
||||||
|
|
||||||
_defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
|
|
||||||
|
|
||||||
_invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
|
|
||||||
|
|
||||||
_passwordResetProviders = passwordResetProviders.ToArray();
|
_passwordResetProviders = passwordResetProviders.ToArray();
|
||||||
|
|
||||||
_defaultPasswordResetProvider = passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
|
_invalidAuthProvider = _authenticationProviders.OfType<InvalidAuthProvider>().First();
|
||||||
|
_defaultAuthenticationProvider = _authenticationProviders.OfType<DefaultAuthenticationProvider>().First();
|
||||||
|
_defaultPasswordResetProvider = _passwordResetProviders.OfType<DefaultPasswordResetProvider>().First();
|
||||||
}
|
}
|
||||||
|
|
||||||
public NameIdPair[] GetAuthenticationProviders()
|
public NameIdPair[] GetAuthenticationProviders()
|
||||||
@ -563,7 +565,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
user.MaxParentalAgeRating = policy.MaxParentalRating;
|
user.MaxParentalAgeRating = policy.MaxParentalRating;
|
||||||
user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
|
user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
|
||||||
user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
|
user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
|
||||||
user.AuthenticationProviderId = policy.AuthenticatioIsnProviderId;
|
user.AuthenticationProviderId = policy.AuthenticationProviderId;
|
||||||
user.PasswordResetProviderId = policy.PasswordResetProviderId;
|
user.PasswordResetProviderId = policy.PasswordResetProviderId;
|
||||||
user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount;
|
user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount;
|
||||||
user.LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1
|
user.LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1
|
||||||
@ -604,28 +606,25 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
|
user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Data.Entities.User CreateUserObject(string name)
|
private bool IsValidUsername(string name)
|
||||||
{
|
{
|
||||||
return new Data.Entities.User(
|
// This is some regex that matches only on unicode "word" characters, as well as -, _ and @
|
||||||
username: name,
|
// In theory this will cut out most if not all 'control' characters which should help minimize any weirdness
|
||||||
mustUpdatePassword: false,
|
// Usernames can contain letters (a-z + whatever else unicode is cool with), numbers (0-9), at-signs (@), dashes (-), underscores (_), apostrophes ('), and periods (.)
|
||||||
authenticationProviderId: _defaultAuthenticationProvider.GetType().FullName,
|
return Regex.IsMatch(name, @"^[\w\-'._@]*$");
|
||||||
invalidLoginAttemptCount: -1,
|
|
||||||
subtitleMode: SubtitlePlaybackMode.Default,
|
|
||||||
playDefaultAudioTrack: true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IAuthenticationProvider GetAuthenticationProvider(Data.Entities.User user)
|
private IAuthenticationProvider GetAuthenticationProvider(User user)
|
||||||
{
|
{
|
||||||
return GetAuthenticationProviders(user)[0];
|
return GetAuthenticationProviders(user)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private IPasswordResetProvider GetPasswordResetProvider(Data.Entities.User user)
|
private IPasswordResetProvider GetPasswordResetProvider(User user)
|
||||||
{
|
{
|
||||||
return GetPasswordResetProviders(user)[0];
|
return GetPasswordResetProviders(user)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList<IAuthenticationProvider> GetAuthenticationProviders(Data.Entities.User user)
|
private IList<IAuthenticationProvider> GetAuthenticationProviders(User user)
|
||||||
{
|
{
|
||||||
var authenticationProviderId = user?.AuthenticationProviderId;
|
var authenticationProviderId = user?.AuthenticationProviderId;
|
||||||
|
|
||||||
@ -640,7 +639,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
{
|
{
|
||||||
// Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
|
// Assign the user to the InvalidAuthProvider since no configured auth provider was valid/found
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"User {UserName} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected",
|
"User {Username} was found with invalid/missing Authentication Provider {AuthenticationProviderId}. Assigning user to InvalidAuthProvider until this is corrected",
|
||||||
user?.Username,
|
user?.Username,
|
||||||
user?.AuthenticationProviderId);
|
user?.AuthenticationProviderId);
|
||||||
providers = new List<IAuthenticationProvider>
|
providers = new List<IAuthenticationProvider>
|
||||||
@ -652,7 +651,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IList<IPasswordResetProvider> GetPasswordResetProviders(Data.Entities.User user)
|
private IList<IPasswordResetProvider> GetPasswordResetProviders(User user)
|
||||||
{
|
{
|
||||||
var passwordResetProviderId = user?.PasswordResetProviderId;
|
var passwordResetProviderId = user?.PasswordResetProviderId;
|
||||||
var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
|
var providers = _passwordResetProviders.Where(i => i.IsEnabled).ToArray();
|
||||||
@ -675,11 +674,10 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
return providers;
|
return providers;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)>
|
private async Task<(IAuthenticationProvider authenticationProvider, string username, bool success)> AuthenticateLocalUser(
|
||||||
AuthenticateLocalUser(
|
|
||||||
string username,
|
string username,
|
||||||
string password,
|
string password,
|
||||||
Jellyfin.Data.Entities.User user,
|
User user,
|
||||||
string remoteEndPoint)
|
string remoteEndPoint)
|
||||||
{
|
{
|
||||||
bool success = false;
|
bool success = false;
|
||||||
@ -721,7 +719,7 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
IAuthenticationProvider provider,
|
IAuthenticationProvider provider,
|
||||||
string username,
|
string username,
|
||||||
string password,
|
string password,
|
||||||
Data.Entities.User resolvedUser)
|
User resolvedUser)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -745,27 +743,21 @@ namespace Jellyfin.Server.Implementations.User
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void IncrementInvalidLoginAttemptCount(Data.Entities.User user)
|
private void IncrementInvalidLoginAttemptCount(User user)
|
||||||
{
|
{
|
||||||
int invalidLogins = user.InvalidLoginAttemptCount;
|
int invalidLogins = user.InvalidLoginAttemptCount;
|
||||||
int? maxInvalidLogins = user.LoginAttemptsBeforeLockout;
|
int? maxInvalidLogins = user.LoginAttemptsBeforeLockout;
|
||||||
if (maxInvalidLogins.HasValue
|
if (maxInvalidLogins.HasValue && invalidLogins >= maxInvalidLogins)
|
||||||
&& invalidLogins >= maxInvalidLogins)
|
|
||||||
{
|
{
|
||||||
user.SetPermission(PermissionKind.IsDisabled, true);
|
user.SetPermission(PermissionKind.IsDisabled, true);
|
||||||
OnUserLockedOut?.Invoke(this, new GenericEventArgs<Data.Entities.User>(user));
|
OnUserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
|
||||||
_logger.LogWarning(
|
_logger.LogWarning(
|
||||||
"Disabling user {UserName} due to {Attempts} unsuccessful login attempts.",
|
"Disabling user {Username} due to {Attempts} unsuccessful login attempts.",
|
||||||
user.Username,
|
user.Username,
|
||||||
invalidLogins);
|
invalidLogins);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateUser(user);
|
UpdateUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ResetInvalidLoginAttemptCount(Data.Entities.User user)
|
|
||||||
{
|
|
||||||
user.InvalidLoginAttemptCount = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,8 +7,10 @@ using Emby.Server.Implementations;
|
|||||||
using Jellyfin.Drawing.Skia;
|
using Jellyfin.Drawing.Skia;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
using Jellyfin.Server.Implementations.Activity;
|
using Jellyfin.Server.Implementations.Activity;
|
||||||
|
using Jellyfin.Server.Implementations.Users;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
@ -69,6 +71,7 @@ namespace Jellyfin.Server
|
|||||||
serviceCollection.AddSingleton<JellyfinDbProvider>();
|
serviceCollection.AddSingleton<JellyfinDbProvider>();
|
||||||
|
|
||||||
serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
|
serviceCollection.AddSingleton<IActivityManager, ActivityManager>();
|
||||||
|
serviceCollection.AddSingleton<IUserManager, UserManager>();
|
||||||
|
|
||||||
base.RegisterServices(serviceCollection);
|
base.RegisterServices(serviceCollection);
|
||||||
}
|
}
|
||||||
@ -80,6 +83,9 @@ namespace Jellyfin.Server
|
|||||||
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
|
protected override IEnumerable<Assembly> GetAssembliesWithPartsInternal()
|
||||||
{
|
{
|
||||||
yield return typeof(CoreAppHost).Assembly;
|
yield return typeof(CoreAppHost).Assembly;
|
||||||
|
yield return typeof(DefaultAuthenticationProvider).Assembly;
|
||||||
|
yield return typeof(DefaultPasswordResetProvider).Assembly;
|
||||||
|
yield return typeof(InvalidAuthProvider).Assembly;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -41,7 +41,6 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="CommandLineParser" Version="2.7.82" />
|
<PackageReference Include="CommandLineParser" Version="2.7.82" />
|
||||||
<PackageReference Include="Json.Net" Version="1.0.22" />
|
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.1.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.1.3" />
|
||||||
<PackageReference Include="prometheus-net" Version="3.5.0" />
|
<PackageReference Include="prometheus-net" Version="3.5.0" />
|
||||||
|
@ -1,33 +1,45 @@
|
|||||||
#pragma warning disable CS1591
|
using System;
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using Emby.Server.Implementations.Data;
|
using Emby.Server.Implementations.Data;
|
||||||
using Emby.Server.Implementations.Serialization;
|
using Emby.Server.Implementations.Serialization;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
using Jellyfin.Server.Implementations;
|
using Jellyfin.Server.Implementations;
|
||||||
|
using Jellyfin.Server.Implementations.Users;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Users;
|
using MediaBrowser.Model.Users;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
|
||||||
using SQLitePCL.pretty;
|
using SQLitePCL.pretty;
|
||||||
|
using JsonSerializer = System.Text.Json.JsonSerializer;
|
||||||
|
|
||||||
namespace Jellyfin.Server.Migrations.Routines
|
namespace Jellyfin.Server.Migrations.Routines
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The migration routine for migrating the user database to EF Core.
|
||||||
|
/// </summary>
|
||||||
public class MigrateUserDb : IMigrationRoutine
|
public class MigrateUserDb : IMigrationRoutine
|
||||||
{
|
{
|
||||||
|
private const string DbFilename = "users.db";
|
||||||
|
|
||||||
private readonly ILogger<MigrateUserDb> _logger;
|
private readonly ILogger<MigrateUserDb> _logger;
|
||||||
|
|
||||||
private readonly IServerApplicationPaths _paths;
|
private readonly IServerApplicationPaths _paths;
|
||||||
|
|
||||||
private readonly JellyfinDbProvider _provider;
|
private readonly JellyfinDbProvider _provider;
|
||||||
|
|
||||||
private readonly MyXmlSerializer _xmlSerializer;
|
private readonly MyXmlSerializer _xmlSerializer;
|
||||||
|
|
||||||
public MigrateUserDb(ILogger<MigrateUserDb> logger, IServerApplicationPaths paths, JellyfinDbProvider provider, MyXmlSerializer xmlSerializer)
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MigrateUserDb"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="logger">The logger.</param>
|
||||||
|
/// <param name="paths">The server application paths.</param>
|
||||||
|
/// <param name="provider">The database provider.</param>
|
||||||
|
/// <param name="xmlSerializer">The xml serializer.</param>
|
||||||
|
public MigrateUserDb(
|
||||||
|
ILogger<MigrateUserDb> logger,
|
||||||
|
IServerApplicationPaths paths,
|
||||||
|
JellyfinDbProvider provider,
|
||||||
|
MyXmlSerializer xmlSerializer)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_paths = paths;
|
_paths = paths;
|
||||||
@ -35,18 +47,21 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
_xmlSerializer = xmlSerializer;
|
_xmlSerializer = xmlSerializer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public Guid Id => Guid.Parse("5C4B82A2-F053-4009-BD05-B6FCAD82F14C");
|
public Guid Id => Guid.Parse("5C4B82A2-F053-4009-BD05-B6FCAD82F14C");
|
||||||
|
|
||||||
public string Name => "MigrateUserDb";
|
/// <inheritdoc/>
|
||||||
|
public string Name => "MigrateUserDatabase";
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
public void Perform()
|
public void Perform()
|
||||||
{
|
{
|
||||||
var dataPath = _paths.DataPath;
|
var dataPath = _paths.DataPath;
|
||||||
_logger.LogInformation("Migrating the user database may take a while, do not stop Jellyfin.");
|
_logger.LogInformation("Migrating the user database may take a while, do not stop Jellyfin.");
|
||||||
|
|
||||||
using (var connection = SQLite3.Open(Path.Combine(dataPath, "users.db"), ConnectionFlags.ReadOnly, null))
|
using (var connection = SQLite3.Open(Path.Combine(dataPath, DbFilename), ConnectionFlags.ReadOnly, null))
|
||||||
{
|
{
|
||||||
using var dbContext = _provider.CreateContext();
|
var dbContext = _provider.CreateContext();
|
||||||
|
|
||||||
var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
|
var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
|
||||||
|
|
||||||
@ -55,26 +70,30 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
|
|
||||||
foreach (var entry in queryResult)
|
foreach (var entry in queryResult)
|
||||||
{
|
{
|
||||||
var json = JsonConvert.DeserializeObject<Dictionary<string, string>>(entry[2].ToString());
|
UserMockup mockup = JsonSerializer.Deserialize<UserMockup>(entry[2].ToBlob());
|
||||||
var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, json["Name"]);
|
var userDataDir = Path.Combine(_paths.UserConfigurationDirectoryPath, mockup.Name);
|
||||||
|
|
||||||
var config = (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), Path.Combine(userDataDir, "config.xml"));
|
var config = File.Exists(Path.Combine(userDataDir, "config.xml"))
|
||||||
var policy = (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), Path.Combine(userDataDir, "policy.xml"));
|
? (UserConfiguration)_xmlSerializer.DeserializeFromFile(typeof(UserConfiguration), Path.Combine(userDataDir, "config.xml"))
|
||||||
|
: new UserConfiguration();
|
||||||
|
var policy = File.Exists(Path.Combine(userDataDir, "policy.xml"))
|
||||||
|
? (UserPolicy)_xmlSerializer.DeserializeFromFile(typeof(UserPolicy), Path.Combine(userDataDir, "policy.xml"))
|
||||||
|
: new UserPolicy();
|
||||||
|
policy.AuthenticationProviderId = policy.AuthenticationProviderId?.Replace(
|
||||||
|
"Emby.Server.Implementations.Library",
|
||||||
|
"Jellyfin.Server.Implementations.Users",
|
||||||
|
StringComparison.Ordinal)
|
||||||
|
?? typeof(DefaultAuthenticationProvider).FullName;
|
||||||
|
|
||||||
var user = new User(
|
policy.PasswordResetProviderId ??= typeof(DefaultPasswordResetProvider).FullName;
|
||||||
json["Name"],
|
|
||||||
false,
|
var user = new User(mockup.Name, policy.AuthenticationProviderId)
|
||||||
policy.AuthenticatioIsnProviderId,
|
|
||||||
policy.InvalidLoginAttemptCount,
|
|
||||||
config.SubtitleMode,
|
|
||||||
config.PlayDefaultAudioTrack)
|
|
||||||
{
|
{
|
||||||
Id = entry[1].ReadGuidFromBlob(),
|
Id = entry[1].ReadGuidFromBlob(),
|
||||||
InternalId = entry[0].ToInt64(),
|
InternalId = entry[0].ToInt64(),
|
||||||
MaxParentalAgeRating = policy.MaxParentalRating,
|
MaxParentalAgeRating = policy.MaxParentalRating,
|
||||||
EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess,
|
EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess,
|
||||||
RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit,
|
RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit,
|
||||||
AuthenticationProviderId = policy.AuthenticatioIsnProviderId,
|
|
||||||
PasswordResetProviderId = policy.PasswordResetProviderId,
|
PasswordResetProviderId = policy.PasswordResetProviderId,
|
||||||
InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount,
|
InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount,
|
||||||
LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1 ? null : new int?(policy.LoginAttemptsBeforeLockout),
|
LoginAttemptsBeforeLockout = policy.LoginAttemptsBeforeLockout == -1 ? null : new int?(policy.LoginAttemptsBeforeLockout),
|
||||||
@ -89,6 +108,10 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay,
|
EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay,
|
||||||
RememberSubtitleSelections = config.RememberSubtitleSelections,
|
RememberSubtitleSelections = config.RememberSubtitleSelections,
|
||||||
SubtitleLanguagePreference = config.SubtitleLanguagePreference,
|
SubtitleLanguagePreference = config.SubtitleLanguagePreference,
|
||||||
|
Password = mockup.Password,
|
||||||
|
EasyPassword = mockup.EasyPassword,
|
||||||
|
LastLoginDate = mockup.LastLoginDate ?? DateTime.MinValue,
|
||||||
|
LastActivityDate = mockup.LastActivityDate ?? DateTime.MinValue
|
||||||
};
|
};
|
||||||
|
|
||||||
user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
|
user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
|
||||||
@ -112,6 +135,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
|
user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
|
||||||
user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
|
user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
|
||||||
user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
|
user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
|
||||||
|
|
||||||
foreach (var policyAccessSchedule in policy.AccessSchedules)
|
foreach (var policyAccessSchedule in policy.AccessSchedules)
|
||||||
{
|
{
|
||||||
user.AccessSchedules.Add(policyAccessSchedule);
|
user.AccessSchedules.Add(policyAccessSchedule);
|
||||||
@ -126,6 +150,8 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
|
user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
|
||||||
user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
|
user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
|
||||||
user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
|
user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
|
||||||
|
|
||||||
|
dbContext.Users.Add(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
dbContext.SaveChanges();
|
dbContext.SaveChanges();
|
||||||
@ -133,12 +159,32 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
File.Move(Path.Combine(dataPath, "users.db"), Path.Combine(dataPath, "users.db" + ".old"));
|
File.Move(Path.Combine(dataPath, DbFilename), Path.Combine(dataPath, DbFilename + ".old"));
|
||||||
|
|
||||||
|
var journalPath = Path.Combine(dataPath, DbFilename + "-journal");
|
||||||
|
if (File.Exists(journalPath))
|
||||||
|
{
|
||||||
|
File.Move(journalPath, Path.Combine(dataPath, DbFilename + ".old-journal"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
_logger.LogError(e, "Error renaming legacy user database to 'users.db.old'");
|
_logger.LogError(e, "Error renaming legacy user database to 'users.db.old'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#nullable disable
|
||||||
|
internal class UserMockup
|
||||||
|
{
|
||||||
|
public string Password { get; set; }
|
||||||
|
|
||||||
|
public string EasyPassword { get; set; }
|
||||||
|
|
||||||
|
public DateTime? LastLoginDate { get; set; }
|
||||||
|
|
||||||
|
public DateTime? LastActivityDate { get; set; }
|
||||||
|
|
||||||
|
public string Name { get; set; }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using System.Xml.Serialization;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using Jellyfin.Data.Enums;
|
using Jellyfin.Data.Enums;
|
||||||
|
|
||||||
@ -34,7 +36,7 @@ namespace MediaBrowser.Model.Users
|
|||||||
|
|
||||||
public string[] BlockedTags { get; set; }
|
public string[] BlockedTags { get; set; }
|
||||||
public bool EnableUserPreferenceAccess { get; set; }
|
public bool EnableUserPreferenceAccess { get; set; }
|
||||||
public Jellyfin.Data.Entities.AccessSchedule[] AccessSchedules { get; set; }
|
public AccessSchedule[] AccessSchedules { get; set; }
|
||||||
public UnratedItem[] BlockUnratedItems { get; set; }
|
public UnratedItem[] BlockUnratedItems { get; set; }
|
||||||
public bool EnableRemoteControlOfOtherUsers { get; set; }
|
public bool EnableRemoteControlOfOtherUsers { get; set; }
|
||||||
public bool EnableSharedDeviceControl { get; set; }
|
public bool EnableSharedDeviceControl { get; set; }
|
||||||
@ -78,7 +80,9 @@ namespace MediaBrowser.Model.Users
|
|||||||
public string[] BlockedChannels { get; set; }
|
public string[] BlockedChannels { get; set; }
|
||||||
|
|
||||||
public int RemoteClientBitrateLimit { get; set; }
|
public int RemoteClientBitrateLimit { get; set; }
|
||||||
public string AuthenticatioIsnProviderId { get; set; }
|
|
||||||
|
[XmlElement(ElementName = "AuthenticationProviderId")]
|
||||||
|
public string AuthenticationProviderId { get; set; }
|
||||||
public string PasswordResetProviderId { get; set; }
|
public string PasswordResetProviderId { get; set; }
|
||||||
|
|
||||||
public UserPolicy()
|
public UserPolicy()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user