diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index 7e67c8a083..8a62a9b27a 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -91,7 +91,7 @@ using Emby.Server.Core.FFMpeg; using Emby.Server.Core.IO; using Emby.Server.Core.Localization; using Emby.Server.Core.Migrations; -using Emby.Server.Core.Security; +using Emby.Server.Implementations.Security; using Emby.Server.Implementations.Social; using Emby.Server.Core.Sync; using Emby.Server.Implementations.Channels; @@ -554,7 +554,7 @@ namespace Emby.Server.Core UserRepository = await GetUserRepository().ConfigureAwait(false); - var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager, JsonSerializer, ApplicationPaths, GetDbConnector(), MemoryStreamFactory); + var displayPreferencesRepo = new SqliteDisplayPreferencesRepository(LogManager.GetLogger("SqliteDisplayPreferencesRepository"), JsonSerializer, ApplicationPaths, MemoryStreamFactory); DisplayPreferencesRepository = displayPreferencesRepo; RegisterSingleInstance(DisplayPreferencesRepository); @@ -699,7 +699,7 @@ namespace Emby.Server.Core SubtitleEncoder = new SubtitleEncoder(LibraryManager, LogManager.GetLogger("SubtitleEncoder"), ApplicationPaths, FileSystemManager, MediaEncoder, JsonSerializer, HttpClient, MediaSourceManager, MemoryStreamFactory, ProcessFactory, textEncoding); RegisterSingleInstance(SubtitleEncoder); - await displayPreferencesRepo.Initialize().ConfigureAwait(false); + displayPreferencesRepo.Initialize(); var userDataRepo = new SqliteUserDataRepository(LogManager, ApplicationPaths, GetDbConnector()); @@ -828,9 +828,9 @@ namespace Emby.Server.Core private async Task GetAuthenticationRepository() { - var repo = new AuthenticationRepository(LogManager, ServerConfigurationManager.ApplicationPaths, GetDbConnector()); + var repo = new AuthenticationRepository(LogManager.GetLogger("AuthenticationRepository"), ServerConfigurationManager.ApplicationPaths); - await repo.Initialize().ConfigureAwait(false); + repo.Initialize(); return repo; } diff --git a/Emby.Server.Core/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Core/Data/SqliteDisplayPreferencesRepository.cs deleted file mode 100644 index a9e63a11d0..0000000000 --- a/Emby.Server.Core/Data/SqliteDisplayPreferencesRepository.cs +++ /dev/null @@ -1,312 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Persistence; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; - -namespace Emby.Server.Core.Data -{ - /// - /// Class SQLiteDisplayPreferencesRepository - /// - public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository - { - private readonly IMemoryStreamFactory _memoryStreamProvider; - - public SqliteDisplayPreferencesRepository(ILogManager logManager, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IDbConnector dbConnector, IMemoryStreamFactory memoryStreamProvider) - : base(logManager, dbConnector) - { - _jsonSerializer = jsonSerializer; - _memoryStreamProvider = memoryStreamProvider; - DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db"); - } - - /// - /// Gets the name of the repository - /// - /// The name. - public string Name - { - get - { - return "SQLite"; - } - } - - /// - /// The _json serializer - /// - private readonly IJsonSerializer _jsonSerializer; - - /// - /// Opens the connection to the database - /// - /// Task. - public async Task Initialize() - { - using (var connection = await CreateConnection().ConfigureAwait(false)) - { - string[] queries = { - - "create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)", - "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)" - }; - - connection.RunQueries(queries, Logger); - } - } - - /// - /// Save the display preferences associated with an item in the repo - /// - /// The display preferences. - /// The user id. - /// The client. - /// The cancellation token. - /// Task. - /// item - public async Task SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken) - { - if (displayPreferences == null) - { - throw new ArgumentNullException("displayPreferences"); - } - if (string.IsNullOrWhiteSpace(displayPreferences.Id)) - { - throw new ArgumentNullException("displayPreferences.Id"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - var serialized = _jsonSerializer.SerializeToBytes(displayPreferences, _memoryStreamProvider); - - using (var connection = await CreateConnection().ConfigureAwait(false)) - { - IDbTransaction transaction = null; - - try - { - transaction = connection.BeginTransaction(); - - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)"; - - cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreferences.Id); - cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId; - cmd.Parameters.Add(cmd, "@3", DbType.String).Value = client; - cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized; - - cmd.Transaction = transaction; - - cmd.ExecuteNonQuery(); - } - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - Logger.ErrorException("Failed to save display preferences:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - } - } - } - - /// - /// Save all display preferences associated with a user in the repo - /// - /// The display preferences. - /// The user id. - /// The cancellation token. - /// Task. - /// item - public async Task SaveAllDisplayPreferences(IEnumerable displayPreferences, Guid userId, CancellationToken cancellationToken) - { - if (displayPreferences == null) - { - throw new ArgumentNullException("displayPreferences"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - using (var connection = await CreateConnection().ConfigureAwait(false)) - { - IDbTransaction transaction = null; - - try - { - transaction = connection.BeginTransaction(); - - foreach (var displayPreference in displayPreferences) - { - - var serialized = _jsonSerializer.SerializeToBytes(displayPreference, _memoryStreamProvider); - - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "replace into userdisplaypreferences (id, userid, client, data) values (@1, @2, @3, @4)"; - - cmd.Parameters.Add(cmd, "@1", DbType.Guid).Value = new Guid(displayPreference.Id); - cmd.Parameters.Add(cmd, "@2", DbType.Guid).Value = userId; - cmd.Parameters.Add(cmd, "@3", DbType.String).Value = displayPreference.Client; - cmd.Parameters.Add(cmd, "@4", DbType.Binary).Value = serialized; - - cmd.Transaction = transaction; - - cmd.ExecuteNonQuery(); - } - } - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - Logger.ErrorException("Failed to save display preferences:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - } - } - } - - /// - /// Gets the display preferences. - /// - /// The display preferences id. - /// The user id. - /// The client. - /// Task{DisplayPreferences}. - /// item - public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client) - { - if (string.IsNullOrWhiteSpace(displayPreferencesId)) - { - throw new ArgumentNullException("displayPreferencesId"); - } - - var guidId = displayPreferencesId.GetMD5(); - - using (var connection = CreateConnection(true).Result) - { - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"; - - cmd.Parameters.Add(cmd, "@id", DbType.Guid).Value = guidId; - cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; - cmd.Parameters.Add(cmd, "@client", DbType.String).Value = client; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) - { - if (reader.Read()) - { - using (var stream = reader.GetMemoryStream(0, _memoryStreamProvider)) - { - return _jsonSerializer.DeserializeFromStream(stream); - } - } - } - - return new DisplayPreferences - { - Id = guidId.ToString("N") - }; - } - } - } - - /// - /// Gets all display preferences for the given user. - /// - /// The user id. - /// Task{DisplayPreferences}. - /// item - public IEnumerable GetAllDisplayPreferences(Guid userId) - { - var list = new List(); - - using (var connection = CreateConnection(true).Result) - { - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = "select data from userdisplaypreferences where userId=@userId"; - - cmd.Parameters.Add(cmd, "@userId", DbType.Guid).Value = userId; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) - { - while (reader.Read()) - { - using (var stream = reader.GetMemoryStream(0, _memoryStreamProvider)) - { - list.Add(_jsonSerializer.DeserializeFromStream(stream)); - } - } - } - } - } - - return list; - } - - public Task SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken) - { - return SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken); - } - - public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client) - { - return GetDisplayPreferences(displayPreferencesId, new Guid(userId), client); - } - } -} \ No newline at end of file diff --git a/Emby.Server.Core/Security/AuthenticationRepository.cs b/Emby.Server.Core/Security/AuthenticationRepository.cs deleted file mode 100644 index 548585375d..0000000000 --- a/Emby.Server.Core/Security/AuthenticationRepository.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Data; -using System.Globalization; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Emby.Server.Core.Data; -using MediaBrowser.Controller; -using MediaBrowser.Controller.Security; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Querying; - -namespace Emby.Server.Core.Security -{ - public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository - { - private readonly IServerApplicationPaths _appPaths; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - public AuthenticationRepository(ILogManager logManager, IServerApplicationPaths appPaths, IDbConnector connector) - : base(logManager, connector) - { - _appPaths = appPaths; - DbFilePath = Path.Combine(appPaths.DataPath, "authentication.db"); - } - - public async Task Initialize() - { - using (var connection = await CreateConnection().ConfigureAwait(false)) - { - string[] queries = { - - "create table if not exists AccessTokens (Id GUID PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT, AppName TEXT, AppVersion TEXT, DeviceName TEXT, UserId TEXT, IsActive BIT, DateCreated DATETIME NOT NULL, DateRevoked DATETIME)", - "create index if not exists idx_AccessTokens on AccessTokens(Id)" - }; - - connection.RunQueries(queries, Logger); - - connection.AddColumn(Logger, "AccessTokens", "AppVersion", "TEXT"); - } - } - - public Task Create(AuthenticationInfo info, CancellationToken cancellationToken) - { - info.Id = Guid.NewGuid().ToString("N"); - - return Update(info, cancellationToken); - } - - public async Task Update(AuthenticationInfo info, CancellationToken cancellationToken) - { - if (info == null) - { - throw new ArgumentNullException("info"); - } - - cancellationToken.ThrowIfCancellationRequested(); - - using (var connection = await CreateConnection().ConfigureAwait(false)) - { - using (var saveInfoCommand = connection.CreateCommand()) - { - saveInfoCommand.CommandText = "replace into AccessTokens (Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked) values (@Id, @AccessToken, @DeviceId, @AppName, @AppVersion, @DeviceName, @UserId, @IsActive, @DateCreated, @DateRevoked)"; - - saveInfoCommand.Parameters.Add(saveInfoCommand, "@Id"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@AccessToken"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@DeviceId"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@AppName"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@AppVersion"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@DeviceName"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@UserId"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@IsActive"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@DateCreated"); - saveInfoCommand.Parameters.Add(saveInfoCommand, "@DateRevoked"); - - IDbTransaction transaction = null; - - try - { - transaction = connection.BeginTransaction(); - - saveInfoCommand.GetParameter("@Id").Value = new Guid(info.Id); - saveInfoCommand.GetParameter("@AccessToken").Value = info.AccessToken; - saveInfoCommand.GetParameter("@DeviceId").Value = info.DeviceId; - saveInfoCommand.GetParameter("@AppName").Value = info.AppName; - saveInfoCommand.GetParameter("@AppVersion").Value = info.AppVersion; - saveInfoCommand.GetParameter("@DeviceName").Value = info.DeviceName; - saveInfoCommand.GetParameter("@UserId").Value = info.UserId; - saveInfoCommand.GetParameter("@IsActive").Value = info.IsActive; - saveInfoCommand.GetParameter("@DateCreated").Value = info.DateCreated; - saveInfoCommand.GetParameter("@DateRevoked").Value = info.DateRevoked; - - saveInfoCommand.Transaction = transaction; - - saveInfoCommand.ExecuteNonQuery(); - - transaction.Commit(); - } - catch (OperationCanceledException) - { - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - catch (Exception e) - { - Logger.ErrorException("Failed to save record:", e); - - if (transaction != null) - { - transaction.Rollback(); - } - - throw; - } - finally - { - if (transaction != null) - { - transaction.Dispose(); - } - } - } - } - } - - private const string BaseSelectText = "select Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked from AccessTokens"; - - public QueryResult Get(AuthenticationInfoQuery query) - { - if (query == null) - { - throw new ArgumentNullException("query"); - } - - using (var connection = CreateConnection(true).Result) - { - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = BaseSelectText; - - var whereClauses = new List(); - - var startIndex = query.StartIndex ?? 0; - - if (!string.IsNullOrWhiteSpace(query.AccessToken)) - { - whereClauses.Add("AccessToken=@AccessToken"); - cmd.Parameters.Add(cmd, "@AccessToken", DbType.String).Value = query.AccessToken; - } - - if (!string.IsNullOrWhiteSpace(query.UserId)) - { - whereClauses.Add("UserId=@UserId"); - cmd.Parameters.Add(cmd, "@UserId", DbType.String).Value = query.UserId; - } - - if (!string.IsNullOrWhiteSpace(query.DeviceId)) - { - whereClauses.Add("DeviceId=@DeviceId"); - cmd.Parameters.Add(cmd, "@DeviceId", DbType.String).Value = query.DeviceId; - } - - if (query.IsActive.HasValue) - { - whereClauses.Add("IsActive=@IsActive"); - cmd.Parameters.Add(cmd, "@IsActive", DbType.Boolean).Value = query.IsActive.Value; - } - - if (query.HasUser.HasValue) - { - if (query.HasUser.Value) - { - whereClauses.Add("UserId not null"); - } - else - { - whereClauses.Add("UserId is null"); - } - } - - var whereTextWithoutPaging = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - if (startIndex > 0) - { - var pagingWhereText = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM AccessTokens {0} ORDER BY DateCreated LIMIT {1})", - pagingWhereText, - startIndex.ToString(_usCulture))); - } - - var whereText = whereClauses.Count == 0 ? - string.Empty : - " where " + string.Join(" AND ", whereClauses.ToArray()); - - cmd.CommandText += whereText; - - cmd.CommandText += " ORDER BY DateCreated"; - - if (query.Limit.HasValue) - { - cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(_usCulture); - } - - cmd.CommandText += "; select count (Id) from AccessTokens" + whereTextWithoutPaging; - - var list = new List(); - var count = 0; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) - { - while (reader.Read()) - { - list.Add(Get(reader)); - } - - if (reader.NextResult() && reader.Read()) - { - count = reader.GetInt32(0); - } - } - - return new QueryResult() - { - Items = list.ToArray(), - TotalRecordCount = count - }; - } - } - } - - public AuthenticationInfo Get(string id) - { - if (string.IsNullOrEmpty(id)) - { - throw new ArgumentNullException("id"); - } - - using (var connection = CreateConnection(true).Result) - { - var guid = new Guid(id); - - using (var cmd = connection.CreateCommand()) - { - cmd.CommandText = BaseSelectText + " where Id=@Id"; - - cmd.Parameters.Add(cmd, "@Id", DbType.Guid).Value = guid; - - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult | CommandBehavior.SingleRow)) - { - if (reader.Read()) - { - return Get(reader); - } - } - } - - return null; - } - } - - private AuthenticationInfo Get(IDataReader reader) - { - var info = new AuthenticationInfo - { - Id = reader.GetGuid(0).ToString("N"), - AccessToken = reader.GetString(1) - }; - - if (!reader.IsDBNull(2)) - { - info.DeviceId = reader.GetString(2); - } - - if (!reader.IsDBNull(3)) - { - info.AppName = reader.GetString(3); - } - - if (!reader.IsDBNull(4)) - { - info.AppVersion = reader.GetString(4); - } - - if (!reader.IsDBNull(5)) - { - info.DeviceName = reader.GetString(5); - } - - if (!reader.IsDBNull(6)) - { - info.UserId = reader.GetString(6); - } - - info.IsActive = reader.GetBoolean(7); - info.DateCreated = reader.GetDateTime(8).ToUniversalTime(); - - if (!reader.IsDBNull(9)) - { - info.DateRevoked = reader.GetDateTime(9).ToUniversalTime(); - } - - return info; - } - } -} diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs index c7ac630a05..8febe83b2e 100644 --- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs +++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs @@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.Data //} db.ExecuteAll(string.Join(";", queries)); - + return db; } @@ -119,5 +119,27 @@ namespace Emby.Server.Implementations.Data { } + + protected void AddColumn(IDatabaseConnection connection, string table, string columnName, string type) + { + foreach (var row in connection.Query("PRAGMA table_info(" + table + ")")) + { + if (row[1].SQLiteType != SQLiteType.Null) + { + var name = row[1].ToString(); + + if (string.Equals(name, columnName, StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + } + + connection.ExecuteAll(string.Join(";", new string[] + { + "alter table " + table, + "add column " + columnName + " " + type + " NULL" + })); + } } } diff --git a/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs new file mode 100644 index 0000000000..79fc893f4c --- /dev/null +++ b/Emby.Server.Implementations/Data/SqliteDisplayPreferencesRepository.cs @@ -0,0 +1,228 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Persistence; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Serialization; +using SQLitePCL.pretty; + +namespace Emby.Server.Implementations.Data +{ + /// + /// Class SQLiteDisplayPreferencesRepository + /// + public class SqliteDisplayPreferencesRepository : BaseSqliteRepository, IDisplayPreferencesRepository + { + private readonly IMemoryStreamFactory _memoryStreamProvider; + + public SqliteDisplayPreferencesRepository(ILogger logger, IJsonSerializer jsonSerializer, IApplicationPaths appPaths, IMemoryStreamFactory memoryStreamProvider) + : base(logger) + { + _jsonSerializer = jsonSerializer; + _memoryStreamProvider = memoryStreamProvider; + DbFilePath = Path.Combine(appPaths.DataPath, "displaypreferences.db"); + } + + /// + /// Gets the name of the repository + /// + /// The name. + public string Name + { + get + { + return "SQLite"; + } + } + + /// + /// The _json serializer + /// + private readonly IJsonSerializer _jsonSerializer; + + /// + /// Opens the connection to the database + /// + /// Task. + public void Initialize() + { + using (var connection = CreateConnection()) + { + string[] queries = { + + "create table if not exists userdisplaypreferences (id GUID, userId GUID, client text, data BLOB)", + "create unique index if not exists userdisplaypreferencesindex on userdisplaypreferences (id, userId, client)" + }; + + connection.RunQueries(queries); + } + } + + /// + /// Save the display preferences associated with an item in the repo + /// + /// The display preferences. + /// The user id. + /// The client. + /// The cancellation token. + /// Task. + /// item + public async Task SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, CancellationToken cancellationToken) + { + if (displayPreferences == null) + { + throw new ArgumentNullException("displayPreferences"); + } + if (string.IsNullOrWhiteSpace(displayPreferences.Id)) + { + throw new ArgumentNullException("displayPreferences.Id"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + lock (WriteLock) + { + using (var connection = CreateConnection()) + { + connection.RunInTransaction(db => + { + SaveDisplayPreferences(displayPreferences, userId, client, db); + }); + } + } + } + + private void SaveDisplayPreferences(DisplayPreferences displayPreferences, Guid userId, string client, IDatabaseConnection connection) + { + var commandText = "replace into userdisplaypreferences (id, userid, client, data) values (?, ?, ?, ?)"; + var serialized = _jsonSerializer.SerializeToBytes(displayPreferences, _memoryStreamProvider); + + connection.Execute(commandText, + displayPreferences.Id.ToGuidParamValue(), + userId.ToGuidParamValue(), + client, + serialized); + } + + /// + /// Save all display preferences associated with a user in the repo + /// + /// The display preferences. + /// The user id. + /// The cancellation token. + /// Task. + /// item + public async Task SaveAllDisplayPreferences(IEnumerable displayPreferences, Guid userId, CancellationToken cancellationToken) + { + if (displayPreferences == null) + { + throw new ArgumentNullException("displayPreferences"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + lock (WriteLock) + { + using (var connection = CreateConnection()) + { + connection.RunInTransaction(db => + { + foreach (var displayPreference in displayPreferences) + { + SaveDisplayPreferences(displayPreference, userId, displayPreference.Client, db); + } + }); + } + } + } + + /// + /// Gets the display preferences. + /// + /// The display preferences id. + /// The user id. + /// The client. + /// Task{DisplayPreferences}. + /// item + public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, Guid userId, string client) + { + if (string.IsNullOrWhiteSpace(displayPreferencesId)) + { + throw new ArgumentNullException("displayPreferencesId"); + } + + var guidId = displayPreferencesId.GetMD5(); + + using (var connection = CreateConnection(true)) + { + var commandText = "select data from userdisplaypreferences where id = ? and userId=? and client=?"; + + var paramList = new List(); + paramList.Add(guidId.ToGuidParamValue()); + paramList.Add(userId.ToGuidParamValue()); + paramList.Add(client); + + foreach (var row in connection.Query(commandText, paramList.ToArray())) + { + return Get(row); + } + + return new DisplayPreferences + { + Id = guidId.ToString("N") + }; + } + } + + /// + /// Gets all display preferences for the given user. + /// + /// The user id. + /// Task{DisplayPreferences}. + /// item + public IEnumerable GetAllDisplayPreferences(Guid userId) + { + var list = new List(); + + using (var connection = CreateConnection(true)) + { + var commandText = "select data from userdisplaypreferences where userId=?"; + + var paramList = new List(); + paramList.Add(userId.ToGuidParamValue()); + + foreach (var row in connection.Query(commandText, paramList.ToArray())) + { + list.Add(Get(row)); + } + } + + return list; + } + + private DisplayPreferences Get(IReadOnlyList row) + { + using (var stream = _memoryStreamProvider.CreateNew(row[0].ToBlob())) + { + stream.Position = 0; + return _jsonSerializer.DeserializeFromStream(stream); + } + } + + public Task SaveDisplayPreferences(DisplayPreferences displayPreferences, string userId, string client, CancellationToken cancellationToken) + { + return SaveDisplayPreferences(displayPreferences, new Guid(userId), client, cancellationToken); + } + + public DisplayPreferences GetDisplayPreferences(string displayPreferencesId, string userId, string client) + { + return GetDisplayPreferences(displayPreferencesId, new Guid(userId), client); + } + } +} \ No newline at end of file diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs index 62615c669a..d9536ae9c5 100644 --- a/Emby.Server.Implementations/Data/SqliteExtensions.cs +++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs @@ -1,5 +1,7 @@ using System; using System.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Serialization; using SQLitePCL.pretty; namespace Emby.Server.Implementations.Data @@ -25,7 +27,12 @@ namespace Emby.Server.Implementations.Data public static byte[] ToGuidParamValue(this string str) { - return new Guid(str).ToByteArray(); + return ToGuidParamValue(new Guid(str)); + } + + public static byte[] ToGuidParamValue(this Guid guid) + { + return guid.ToByteArray(); } public static Guid ReadGuid(this IResultSetValue result) @@ -101,5 +108,24 @@ namespace Emby.Server.Implementations.Data DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None).ToUniversalTime(); } + + /// + /// Serializes to bytes. + /// + /// System.Byte[][]. + /// obj + public static byte[] SerializeToBytes(this IJsonSerializer json, object obj, IMemoryStreamFactory streamProvider) + { + if (obj == null) + { + throw new ArgumentNullException("obj"); + } + + using (var stream = streamProvider.CreateNew()) + { + json.SerializeToStream(obj, stream); + return stream.ToArray(); + } + } } } diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 6843ad9d73..a4f26bc601 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -50,6 +50,7 @@ + @@ -192,6 +193,7 @@ + diff --git a/Emby.Server.Implementations/Security/AuthenticationRepository.cs b/Emby.Server.Implementations/Security/AuthenticationRepository.cs new file mode 100644 index 0000000000..5179bd2581 --- /dev/null +++ b/Emby.Server.Implementations/Security/AuthenticationRepository.cs @@ -0,0 +1,258 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Emby.Server.Implementations.Data; +using MediaBrowser.Controller; +using MediaBrowser.Controller.Security; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Querying; +using SQLitePCL.pretty; + +namespace Emby.Server.Implementations.Security +{ + public class AuthenticationRepository : BaseSqliteRepository, IAuthenticationRepository + { + private readonly IServerApplicationPaths _appPaths; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + + public AuthenticationRepository(ILogger logger, IServerApplicationPaths appPaths) + : base(logger) + { + _appPaths = appPaths; + DbFilePath = Path.Combine(appPaths.DataPath, "authentication.db"); + } + + public void Initialize() + { + using (var connection = CreateConnection()) + { + string[] queries = { + + "create table if not exists AccessTokens (Id GUID PRIMARY KEY, AccessToken TEXT NOT NULL, DeviceId TEXT, AppName TEXT, AppVersion TEXT, DeviceName TEXT, UserId TEXT, IsActive BIT, DateCreated DATETIME NOT NULL, DateRevoked DATETIME)", + "create index if not exists idx_AccessTokens on AccessTokens(Id)" + }; + + connection.RunQueries(queries); + + connection.RunInTransaction(db => + { + AddColumn(db, "AccessTokens", "AppVersion", "TEXT"); + }); + } + } + + public Task Create(AuthenticationInfo info, CancellationToken cancellationToken) + { + info.Id = Guid.NewGuid().ToString("N"); + + return Update(info, cancellationToken); + } + + public async Task Update(AuthenticationInfo info, CancellationToken cancellationToken) + { + if (info == null) + { + throw new ArgumentNullException("info"); + } + + cancellationToken.ThrowIfCancellationRequested(); + + lock (WriteLock) + { + using (var connection = CreateConnection()) + { + connection.RunInTransaction(db => + { + var commandText = "replace into AccessTokens (Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; + + db.Execute(commandText, + info.Id.ToGuidParamValue(), + info.AccessToken, + info.DeviceId, + info.AppName, + info.AppVersion, + info.DeviceName, + info.UserId, + info.IsActive, + info.DateCreated.ToDateTimeParamValue(), + info.DateRevoked.HasValue ? info.DateRevoked.Value.ToDateTimeParamValue() : null); + }); + } + } + } + + private const string BaseSelectText = "select Id, AccessToken, DeviceId, AppName, AppVersion, DeviceName, UserId, IsActive, DateCreated, DateRevoked from AccessTokens"; + + public QueryResult Get(AuthenticationInfoQuery query) + { + if (query == null) + { + throw new ArgumentNullException("query"); + } + + using (var connection = CreateConnection(true)) + { + var commandText = BaseSelectText; + var paramList = new List(); + + var whereClauses = new List(); + + var startIndex = query.StartIndex ?? 0; + + if (!string.IsNullOrWhiteSpace(query.AccessToken)) + { + whereClauses.Add("AccessToken=?"); + paramList.Add(query.AccessToken); + } + + if (!string.IsNullOrWhiteSpace(query.UserId)) + { + whereClauses.Add("UserId=?"); + paramList.Add(query.UserId); + } + + if (!string.IsNullOrWhiteSpace(query.DeviceId)) + { + whereClauses.Add("DeviceId=?"); + paramList.Add(query.DeviceId); + } + + if (query.IsActive.HasValue) + { + whereClauses.Add("IsActive=?"); + paramList.Add(query.IsActive.Value); + } + + if (query.HasUser.HasValue) + { + if (query.HasUser.Value) + { + whereClauses.Add("UserId not null"); + } + else + { + whereClauses.Add("UserId is null"); + } + } + + var whereTextWithoutPaging = whereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", whereClauses.ToArray()); + + if (startIndex > 0) + { + var pagingWhereText = whereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", whereClauses.ToArray()); + + whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM AccessTokens {0} ORDER BY DateCreated LIMIT {1})", + pagingWhereText, + startIndex.ToString(_usCulture))); + } + + var whereText = whereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", whereClauses.ToArray()); + + commandText += whereText; + + commandText += " ORDER BY DateCreated"; + + if (query.Limit.HasValue) + { + commandText += " LIMIT " + query.Limit.Value.ToString(_usCulture); + } + + var list = new List(); + + foreach (var row in connection.Query(commandText, paramList.ToArray())) + { + list.Add(Get(row)); + } + + var count = connection.Query("select count (Id) from AccessTokens" + whereTextWithoutPaging, paramList.ToArray()) + .SelectScalarInt() + .First(); + + return new QueryResult() + { + Items = list.ToArray(), + TotalRecordCount = count + }; + } + } + + public AuthenticationInfo Get(string id) + { + if (string.IsNullOrEmpty(id)) + { + throw new ArgumentNullException("id"); + } + + lock (WriteLock) + { + using (var connection = CreateConnection(true)) + { + var commandText = BaseSelectText + " where Id=?"; + var paramList = new List(); + + paramList.Add(id.ToGuidParamValue()); + + foreach (var row in connection.Query(commandText, paramList.ToArray())) + { + return Get(row); + } + return null; + } + } + } + + private AuthenticationInfo Get(IReadOnlyList reader) + { + var info = new AuthenticationInfo + { + Id = reader[0].ReadGuid().ToString("N"), + AccessToken = reader[1].ToString() + }; + + if (reader[2].SQLiteType != SQLiteType.Null) + { + info.DeviceId = reader[2].ToString(); + } + + if (reader[3].SQLiteType != SQLiteType.Null) + { + info.AppName = reader[3].ToString(); + } + + if (reader[4].SQLiteType != SQLiteType.Null) + { + info.AppVersion = reader[4].ToString(); + } + + if (reader[5].SQLiteType != SQLiteType.Null) + { + info.DeviceName = reader[5].ToString(); + } + + if (reader[6].SQLiteType != SQLiteType.Null) + { + info.UserId = reader[6].ToString(); + } + + info.IsActive = reader[7].ToBool(); + info.DateCreated = reader[8].ReadDateTime(); + + if (reader[9].SQLiteType != SQLiteType.Null) + { + info.DateRevoked = reader[9].ReadDateTime(); + } + + return info; + } + } +}