From d7972704dd7374c8dbc3b089a6bbbfe39057495b Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Sun, 9 May 2021 02:38:54 +0200 Subject: [PATCH] Working oidc --- Kyoo.Authentication/AuthenticationModule.cs | 50 +++++-- .../AuthorizationValidatorHandler.cs | 2 +- Kyoo.Authentication/Controllers/UserStore.cs | 133 ++++++++++++++++++ Kyoo.Authentication/IdentityContext.cs | 12 +- .../Models/Options/PermissionOption.cs | 2 - Kyoo.Authentication/Views/AccountApi.cs | 35 +++-- Kyoo.Common/Controllers/ILibraryManager.cs | 20 ++- Kyoo.Common/Controllers/IRepository.cs | 3 +- .../Exceptions/DuplicatedItemException.cs | 10 ++ .../Exceptions/ItemNotFoundException.cs | 10 ++ Kyoo.Common/Models/LibraryItem.cs | 3 +- Kyoo.Common/Models/Link.cs | 2 + Kyoo.Common/Models/MetadataID.cs | 9 -- Kyoo.Common/Models/PeopleRole.cs | 23 --- Kyoo.Common/Models/Resources/Episode.cs | 43 ------ Kyoo.Common/Models/Resources/Library.cs | 11 -- Kyoo.Common/Models/Resources/People.cs | 11 -- Kyoo.Common/Models/Resources/Season.cs | 20 --- Kyoo.Common/Models/Resources/Show.cs | 56 -------- Kyoo.CommonAPI/CrudApi.cs | 22 +-- Kyoo.CommonAPI/DatabaseContext.cs | 1 - Kyoo.CommonAPI/LocalRepository.cs | 14 +- Kyoo.Postgresql/PostgresModule.cs | 2 +- Kyoo.Tests/Library/SetupTests.cs | 3 - Kyoo.WebApp | 2 +- .../Repositories/EpisodeRepository.cs | 2 +- .../Repositories/LibraryItemRepository.cs | 7 +- .../Repositories/LibraryRepository.cs | 5 +- .../Repositories/PeopleRepository.cs | 12 +- .../Repositories/SeasonRepository.cs | 2 +- .../Repositories/ShowRepository.cs | 8 +- .../Repositories/TrackRepository.cs | 3 +- Kyoo/Program.cs | 5 +- Kyoo/Startup.cs | 3 +- Kyoo/Tasks/Crawler.cs | 21 ++- Kyoo/Views/CollectionApi.cs | 4 +- Kyoo/Views/EpisodeApi.cs | 14 +- Kyoo/Views/GenreApi.cs | 3 +- Kyoo/Views/LibraryApi.cs | 9 +- Kyoo/Views/PeopleApi.cs | 10 +- Kyoo/Views/ProviderApi.cs | 11 +- Kyoo/Views/SeasonApi.cs | 17 ++- Kyoo/Views/ShowApi.cs | 27 ++-- Kyoo/Views/StudioApi.cs | 4 +- Kyoo/settings.json | 9 +- 45 files changed, 347 insertions(+), 328 deletions(-) create mode 100644 Kyoo.Authentication/Controllers/UserStore.cs diff --git a/Kyoo.Authentication/AuthenticationModule.cs b/Kyoo.Authentication/AuthenticationModule.cs index 559517d2..26b50dd1 100644 --- a/Kyoo.Authentication/AuthenticationModule.cs +++ b/Kyoo.Authentication/AuthenticationModule.cs @@ -3,16 +3,19 @@ using System.Collections.Generic; using IdentityServer4.Extensions; using IdentityServer4.Services; using Kyoo.Authentication.Models; +using Kyoo.Authentication.Views; using Kyoo.Controllers; -using Kyoo.Models; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using Microsoft.IdentityModel.Logging; namespace Kyoo.Authentication { @@ -53,16 +56,25 @@ namespace Kyoo.Authentication /// private readonly ILoggerFactory _loggerFactory; - + /// + /// The environment information to check if the app runs in debug mode + /// + private readonly IWebHostEnvironment _environment; + + /// /// Create a new authentication module instance and use the given configuration and environment. /// /// The configuration to use /// The logger factory to allow IdentityServer to log things - public AuthenticationModule(IConfiguration configuration, ILoggerFactory loggerFactory) + /// The environment information to check if the app runs in debug mode + public AuthenticationModule(IConfiguration configuration, + ILoggerFactory loggerFactory, + IWebHostEnvironment environment) { _configuration = configuration; _loggerFactory = loggerFactory; + _environment = environment; } /// @@ -70,8 +82,16 @@ namespace Kyoo.Authentication { string publicUrl = _configuration.GetValue("public_url").TrimEnd('/'); + if (_environment.IsDevelopment()) + IdentityModelEventSource.ShowPII = true; + services.AddControllers(); + // services.AddIdentityCore() + // .AddSignInManager() + // .AddDefaultTokenProviders() + // .AddUserStore(); + // services.AddDbContext(options => // { // options.UseNpgsql(_configuration.GetDatabaseConnection("postgres")); @@ -113,25 +133,25 @@ namespace Kyoo.Authentication // options.EnableTokenCleanup = true; // }) .AddInMemoryIdentityResources(IdentityContext.GetIdentityResources()) + .AddInMemoryApiScopes(IdentityContext.GetScopes()) .AddInMemoryApiResources(IdentityContext.GetApis()) .AddInMemoryClients(IdentityContext.GetClients()) - .AddDeveloperSigningCredential(); - // .AddProfileService() - // .AddSigninKeys(certificateOptions); + .AddProfileService() + .AddSigninKeys(certificateOptions); // TODO implement means to add clients or api scopes for other plugins. // TODO split scopes (kyoo.read should be task.read, video.read etc) - services.AddAuthentication(o => - { - o.DefaultScheme = IdentityConstants.ApplicationScheme; - o.DefaultSignInScheme = IdentityConstants.ExternalScheme; - }) - .AddIdentityCookies(_ => { }); + // services.AddAuthentication(o => + // { + // o.DefaultScheme = IdentityConstants.ApplicationScheme; + // o.DefaultSignInScheme = IdentityConstants.ExternalScheme; + // }) + // .AddIdentityCookies(_ => { }); services.AddAuthentication() .AddJwtBearer(options => { options.Authority = publicUrl; - options.Audience = "Kyoo"; + options.Audience = "kyoo"; options.RequireHttpsMetadata = false; }); @@ -146,10 +166,10 @@ namespace Kyoo.Authentication { options.AddPolicy(permission, policy => { - policy.AuthenticationSchemes.Add(IdentityConstants.ApplicationScheme); + // policy.AuthenticationSchemes.Add(IdentityConstants.ApplicationScheme); policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme); policy.AddRequirements(new AuthRequirement(permission)); - policy.RequireScope($"kyoo.{permission.ToLower()}"); + // policy.RequireScope($"kyoo.{permission.ToLower()}"); }); } }); diff --git a/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs b/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs index 00df30db..1df1c86a 100644 --- a/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs +++ b/Kyoo.Authentication/Controllers/AuthorizationValidatorHandler.cs @@ -41,7 +41,7 @@ namespace Kyoo.Authentication else { ICollection defaultPerms = _options.CurrentValue.Default; - if (defaultPerms.Contains(requirement.Permission.ToLower())) + if (defaultPerms?.Contains(requirement.Permission.ToLower()) == true) context.Succeed(requirement); } diff --git a/Kyoo.Authentication/Controllers/UserStore.cs b/Kyoo.Authentication/Controllers/UserStore.cs new file mode 100644 index 00000000..0649796f --- /dev/null +++ b/Kyoo.Authentication/Controllers/UserStore.cs @@ -0,0 +1,133 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Kyoo.Controllers; +using Kyoo.Models; +using Microsoft.AspNetCore.Identity; + +namespace Kyoo.Authentication +{ + /// + /// An implementation of an that uses an . + /// + public class UserStore : IUserStore + { + /// + /// The user repository used to store users. + /// + private readonly IUserRepository _users; + + /// + /// Create a new . + /// + /// The user repository to use + public UserStore(IUserRepository users) + { + _users = users; + } + + + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Implementation of the IDisposable pattern + /// + /// True if this class should be disposed. + protected virtual void Dispose(bool disposing) + { + bool _ = disposing; + // Not implemented because this class has nothing to dispose. + } + + /// + public Task GetUserIdAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.ID.ToString()); + } + + /// + public Task GetUserNameAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.Username); + } + + /// + public Task SetUserNameAsync(User user, string userName, CancellationToken cancellationToken) + { + user.Username = userName; + return Task.CompletedTask; + } + + /// + public Task GetNormalizedUserNameAsync(User user, CancellationToken cancellationToken) + { + return Task.FromResult(user.Slug); + } + + /// + public Task SetNormalizedUserNameAsync(User user, string normalizedName, CancellationToken cancellationToken) + { + user.Slug = normalizedName; + return Task.CompletedTask; + } + + /// + public async Task CreateAsync(User user, CancellationToken cancellationToken) + { + try + { + await _users.Create(user); + return IdentityResult.Success; + } + catch (Exception ex) + { + return IdentityResult.Failed(new IdentityError {Code = ex.GetType().Name, Description = ex.Message}); + } + } + + /// + public async Task UpdateAsync(User user, CancellationToken cancellationToken) + { + try + { + await _users.Edit(user, false); + return IdentityResult.Success; + } + catch (Exception ex) + { + return IdentityResult.Failed(new IdentityError {Code = ex.GetType().Name, Description = ex.Message}); + } + } + + /// + public async Task DeleteAsync(User user, CancellationToken cancellationToken) + { + try + { + await _users.Delete(user); + return IdentityResult.Success; + } + catch (Exception ex) + { + return IdentityResult.Failed(new IdentityError {Code = ex.GetType().Name, Description = ex.Message}); + } + } + + /// + public Task FindByIdAsync(string userId, CancellationToken cancellationToken) + { + return _users.GetOrDefault(int.Parse(userId)); + } + + /// + public Task FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken) + { + return _users.GetOrDefault(normalizedUserName); + } + } +} \ No newline at end of file diff --git a/Kyoo.Authentication/IdentityContext.cs b/Kyoo.Authentication/IdentityContext.cs index 21d649e1..071a601b 100644 --- a/Kyoo.Authentication/IdentityContext.cs +++ b/Kyoo.Authentication/IdentityContext.cs @@ -23,7 +23,7 @@ namespace Kyoo.Authentication new() { ClientId = "kyoo.webapp", - + AccessTokenType = AccessTokenType.Jwt, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, @@ -33,7 +33,7 @@ namespace Kyoo.Authentication AllowOfflineAccess = true, RequireConsent = false, - AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.download", "kyoo.admin" }, + AllowedScopes = { "openid", "profile", "kyoo.read", "kyoo.write", "kyoo.play", "kyoo.admin" }, RedirectUris = { "/", "/silent.html" }, PostLogoutRedirectUris = { "/logout" } } @@ -60,11 +60,6 @@ namespace Kyoo.Authentication DisplayName = "Allow playback of movies and episodes." }, new ApiScope - { - Name = "kyoo.download", - DisplayName = "Allow downloading of episodes and movies from kyoo." - }, - new ApiScope { Name = "kyoo.admin", DisplayName = "Full access to the admin's API and the public API." @@ -76,9 +71,8 @@ namespace Kyoo.Authentication { return new[] { - new ApiResource + new ApiResource("kyoo", "Kyoo") { - Name = "Kyoo", Scopes = GetScopes().Select(x => x.Name).ToArray() } }; diff --git a/Kyoo.Authentication/Models/Options/PermissionOption.cs b/Kyoo.Authentication/Models/Options/PermissionOption.cs index 0a26f89e..8d6c698d 100644 --- a/Kyoo.Authentication/Models/Options/PermissionOption.cs +++ b/Kyoo.Authentication/Models/Options/PermissionOption.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Kyoo.Authentication.Models { /// diff --git a/Kyoo.Authentication/Views/AccountApi.cs b/Kyoo.Authentication/Views/AccountApi.cs index bd49da94..81bc3a55 100644 --- a/Kyoo.Authentication/Views/AccountApi.cs +++ b/Kyoo.Authentication/Views/AccountApi.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Security.Claims; using System.Threading.Tasks; +using IdentityServer4; using IdentityServer4.Extensions; using IdentityServer4.Models; using IdentityServer4.Services; @@ -14,6 +16,7 @@ using Kyoo.Models; using Kyoo.Models.Exceptions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; @@ -39,6 +42,7 @@ namespace Kyoo.Authentication.Views /// A file manager to send profile pictures /// private readonly IFileManager _files; + // private readonly SignInManager _signInManager; /// /// Options about authentication. Those options are monitored and reloads are supported. @@ -57,11 +61,13 @@ namespace Kyoo.Authentication.Views // IIdentityServerInteractionService interaction, IFileManager files, IOptions options) + //, SignInManager signInManager) { _users = users; // _interaction = interaction; _files = files; _options = options; + // _signInManager = signInManager; } @@ -114,14 +120,14 @@ namespace Kyoo.Authentication.Views public async Task Login([FromBody] LoginRequest login) { // AuthorizationRequest context = await _interaction.GetAuthorizationContextAsync(login.ReturnURL); - User user = await _users.Get(x => x.Username == login.Username); + User user = await _users.GetOrDefault(x => x.Username == login.Username); if (user == null) return Unauthorized(); if (!PasswordUtils.CheckPassword(login.Password, user.Password)) return Unauthorized(); - await HttpContext.SignInAsync(user.ID.ToString(), user.ToPrincipal(), StayLogged(login.StayLoggedIn)); + // await _signInManager.SignInAsync(user, login.StayLoggedIn); return Ok(new { RedirectUrl = login.ReturnURL, IsOk = true }); } @@ -142,7 +148,15 @@ namespace Kyoo.Authentication.Views { code = "ExpiredOTAC", description = "The OTAC has expired. Try to login with your password." }); - await HttpContext.SignInAsync(user.ID.ToString(), user.ToPrincipal(), StayLogged(otac.StayLoggedIn)); + + + IdentityServerUser iduser = new(user.ID.ToString()) + { + DisplayName = user.Username + }; + + await HttpContext.SignInAsync(iduser, StayLogged(otac.StayLoggedIn)); + // await _signInManager.SignInAsync(user, otac.StayLoggedIn); return Ok(); } @@ -153,22 +167,23 @@ namespace Kyoo.Authentication.Views [Authorize] public async Task Logout() { - await HttpContext.SignOutAsync(); + // await _signInManager.SignOutAsync(); return Ok(); } // TODO check with the extension method public async Task GetProfileDataAsync(ProfileDataRequestContext context) { - User user = await _users.Get(int.Parse(context.Subject.GetSubjectId())); + User user = await _users.GetOrDefault(int.Parse(context.Subject.GetSubjectId())); if (user == null) return; context.IssuedClaims.AddRange(user.GetClaims()); + context.IssuedClaims.Add(new Claim("permissions", string.Join(',', user.Permissions))); } public async Task IsActiveAsync(IsActiveContext context) { - User user = await _users.Get(int.Parse(context.Subject.GetSubjectId())); + User user = await _users.GetOrDefault(int.Parse(context.Subject.GetSubjectId())); context.IsActive = user != null; } @@ -186,8 +201,10 @@ namespace Kyoo.Authentication.Views [Authorize] public async Task> Update([FromForm] AccountUpdateRequest data) { - User user = await _users.Get(int.Parse(HttpContext.User.GetSubjectId())); - + User user = await _users.GetOrDefault(int.Parse(HttpContext.User.GetSubjectId())); + + if (user == null) + return Unauthorized(); if (!string.IsNullOrEmpty(data.Email)) user.Email = data.Email; if (!string.IsNullOrEmpty(data.Username)) @@ -204,7 +221,7 @@ namespace Kyoo.Authentication.Views [HttpGet("permissions")] public ActionResult> GetDefaultPermissions() { - return _options.Value.Permissions.Default; + return _options.Value.Permissions.Default ?? Array.Empty(); } } } \ No newline at end of file diff --git a/Kyoo.Common/Controllers/ILibraryManager.cs b/Kyoo.Common/Controllers/ILibraryManager.cs index 6c100622..2cd0c909 100644 --- a/Kyoo.Common/Controllers/ILibraryManager.cs +++ b/Kyoo.Common/Controllers/ILibraryManager.cs @@ -84,6 +84,7 @@ namespace Kyoo.Controllers /// The type of the resource /// If the item is not found /// The resource found + [ItemNotNull] Task Get(int id) where T : class, IResource; /// @@ -93,6 +94,7 @@ namespace Kyoo.Controllers /// The type of the resource /// If the item is not found /// The resource found + [ItemNotNull] Task Get(string slug) where T : class, IResource; /// @@ -102,6 +104,7 @@ namespace Kyoo.Controllers /// The type of the resource /// If the item is not found /// The first resource found that match the where function + [ItemNotNull] Task Get(Expression> where) where T : class, IResource; /// @@ -111,6 +114,7 @@ namespace Kyoo.Controllers /// The season's number /// If the item is not found /// The season found + [ItemNotNull] Task Get(int showID, int seasonNumber); /// @@ -120,6 +124,7 @@ namespace Kyoo.Controllers /// The season's number /// If the item is not found /// The season found + [ItemNotNull] Task Get(string showSlug, int seasonNumber); /// @@ -130,6 +135,7 @@ namespace Kyoo.Controllers /// The episode's number /// If the item is not found /// The episode found + [ItemNotNull] Task Get(int showID, int seasonNumber, int episodeNumber); /// @@ -140,6 +146,7 @@ namespace Kyoo.Controllers /// The episode's number /// If the item is not found /// The episode found + [ItemNotNull] Task Get(string showSlug, int seasonNumber, int episodeNumber); /// @@ -148,7 +155,8 @@ namespace Kyoo.Controllers /// The slug of the track /// The type (Video, Audio or Subtitle) /// If the item is not found - /// The tracl found + /// The track found + [ItemNotNull] Task Get(string slug, StreamType type = StreamType.Unknown); /// @@ -157,6 +165,7 @@ namespace Kyoo.Controllers /// The id of the resource /// The type of the resource /// The resource found + [ItemCanBeNull] Task GetOrDefault(int id) where T : class, IResource; /// @@ -165,6 +174,7 @@ namespace Kyoo.Controllers /// The slug of the resource /// The type of the resource /// The resource found + [ItemCanBeNull] Task GetOrDefault(string slug) where T : class, IResource; /// @@ -173,6 +183,7 @@ namespace Kyoo.Controllers /// The filter function. /// The type of the resource /// The first resource found that match the where function + [ItemCanBeNull] Task GetOrDefault(Expression> where) where T : class, IResource; /// @@ -181,6 +192,7 @@ namespace Kyoo.Controllers /// The id of the show /// The season's number /// The season found + [ItemCanBeNull] Task GetOrDefault(int showID, int seasonNumber); /// @@ -189,6 +201,7 @@ namespace Kyoo.Controllers /// The slug of the show /// The season's number /// The season found + [ItemCanBeNull] Task GetOrDefault(string showSlug, int seasonNumber); /// @@ -198,6 +211,7 @@ namespace Kyoo.Controllers /// The season's number /// The episode's number /// The episode found + [ItemCanBeNull] Task GetOrDefault(int showID, int seasonNumber, int episodeNumber); /// @@ -207,6 +221,7 @@ namespace Kyoo.Controllers /// The season's number /// The episode's number /// The episode found + [ItemCanBeNull] Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber); /// @@ -214,7 +229,8 @@ namespace Kyoo.Controllers /// /// The slug of the track /// The type (Video, Audio or Subtitle) - /// The tracl found + /// The track found + [ItemCanBeNull] Task GetOrDefault(string slug, StreamType type = StreamType.Unknown); diff --git a/Kyoo.Common/Controllers/IRepository.cs b/Kyoo.Common/Controllers/IRepository.cs index 4e8b16a3..dad2d5e3 100644 --- a/Kyoo.Common/Controllers/IRepository.cs +++ b/Kyoo.Common/Controllers/IRepository.cs @@ -212,9 +212,8 @@ namespace Kyoo.Controllers /// Create a new resource if it does not exist already. If it does, the existing value is returned instead. /// /// The object to create - /// Allow issues to occurs in this method. Every issue is caught and ignored. /// The newly created item or the existing value if it existed. - Task CreateIfNotExists([NotNull] T obj, bool silentFail = false); + Task CreateIfNotExists([NotNull] T obj); /// /// Edit a resource diff --git a/Kyoo.Common/Models/Exceptions/DuplicatedItemException.cs b/Kyoo.Common/Models/Exceptions/DuplicatedItemException.cs index eb85432f..b0d26bf0 100644 --- a/Kyoo.Common/Models/Exceptions/DuplicatedItemException.cs +++ b/Kyoo.Common/Models/Exceptions/DuplicatedItemException.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; namespace Kyoo.Models.Exceptions { @@ -22,5 +23,14 @@ namespace Kyoo.Models.Exceptions public DuplicatedItemException(string message) : base(message) { } + + /// + /// The serialization constructor + /// + /// Serialization infos + /// The serialization context + protected DuplicatedItemException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Exceptions/ItemNotFoundException.cs b/Kyoo.Common/Models/Exceptions/ItemNotFoundException.cs index a04b60a1..d05882b1 100644 --- a/Kyoo.Common/Models/Exceptions/ItemNotFoundException.cs +++ b/Kyoo.Common/Models/Exceptions/ItemNotFoundException.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; namespace Kyoo.Models.Exceptions { @@ -20,5 +21,14 @@ namespace Kyoo.Models.Exceptions public ItemNotFoundException(string message) : base(message) { } + + /// + /// The serialization constructor + /// + /// Serialization infos + /// The serialization context + protected ItemNotFoundException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } } } \ No newline at end of file diff --git a/Kyoo.Common/Models/LibraryItem.cs b/Kyoo.Common/Models/LibraryItem.cs index 6fe964d4..78f604f2 100644 --- a/Kyoo.Common/Models/LibraryItem.cs +++ b/Kyoo.Common/Models/LibraryItem.cs @@ -1,5 +1,6 @@ using System; using System.Linq.Expressions; +using JetBrains.Annotations; using Kyoo.Models.Attributes; namespace Kyoo.Models @@ -22,7 +23,7 @@ namespace Kyoo.Models public int? StartYear { get; set; } public int? EndYear { get; set; } [SerializeAs("{HOST}/api/{_type}/{Slug}/poster")] public string Poster { get; set; } - private string _type => Type == ItemType.Collection ? "collection" : "show"; + [UsedImplicitly] private string _type => Type == ItemType.Collection ? "collection" : "show"; public ItemType Type { get; set; } public LibraryItem() {} diff --git a/Kyoo.Common/Models/Link.cs b/Kyoo.Common/Models/Link.cs index f504b5a0..2df85f1f 100644 --- a/Kyoo.Common/Models/Link.cs +++ b/Kyoo.Common/Models/Link.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace Kyoo.Models @@ -60,6 +61,7 @@ namespace Kyoo.Models public Link() {} + [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] public Link(T1 first, T2 second, bool privateItems = false) : base(first, second) { diff --git a/Kyoo.Common/Models/MetadataID.cs b/Kyoo.Common/Models/MetadataID.cs index cc7985ac..d1752d50 100644 --- a/Kyoo.Common/Models/MetadataID.cs +++ b/Kyoo.Common/Models/MetadataID.cs @@ -22,14 +22,5 @@ namespace Kyoo.Models public string DataID { get; set; } public string Link { get; set; } - - public MetadataID() { } - - public MetadataID(Provider provider, string dataID, string link) - { - Provider = provider; - DataID = dataID; - Link = link; - } } } \ No newline at end of file diff --git a/Kyoo.Common/Models/PeopleRole.cs b/Kyoo.Common/Models/PeopleRole.cs index fe027682..be48abd1 100644 --- a/Kyoo.Common/Models/PeopleRole.cs +++ b/Kyoo.Common/Models/PeopleRole.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using Kyoo.Models.Attributes; namespace Kyoo.Models @@ -14,27 +13,5 @@ namespace Kyoo.Models [SerializeIgnore] public virtual Show Show { get; set; } public string Role { get; set; } public string Type { get; set; } - - public PeopleRole() {} - - public PeopleRole(People people, Show show, string role, string type) - { - People = people; - Show = show; - Role = role; - Type = type; - } - - public PeopleRole(string slug, - string name, - string role, - string type, - string poster, - IEnumerable externalIDs) - { - People = new People(slug, name, poster, externalIDs); - Role = role; - Type = type; - } } } \ No newline at end of file diff --git a/Kyoo.Common/Models/Resources/Episode.cs b/Kyoo.Common/Models/Resources/Episode.cs index 27feefd3..29aeab06 100644 --- a/Kyoo.Common/Models/Resources/Episode.cs +++ b/Kyoo.Common/Models/Resources/Episode.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Linq; using Kyoo.Models.Attributes; namespace Kyoo.Models @@ -32,48 +31,6 @@ namespace Kyoo.Models [EditableRelation] [LoadableRelation] public virtual ICollection Tracks { get; set; } - public Episode() { } - - public Episode(int seasonNumber, - int episodeNumber, - int absoluteNumber, - string title, - string overview, - DateTime? releaseDate, - int runtime, - string thumb, - IEnumerable externalIDs) - { - SeasonNumber = seasonNumber; - EpisodeNumber = episodeNumber; - AbsoluteNumber = absoluteNumber; - Title = title; - Overview = overview; - ReleaseDate = releaseDate; - Runtime = runtime; - Thumb = thumb; - ExternalIDs = externalIDs?.ToArray(); - } - - public Episode(int showID, - int seasonID, - int seasonNumber, - int episodeNumber, - int absoluteNumber, - string path, - string title, - string overview, - DateTime? releaseDate, - int runtime, - string poster, - IEnumerable externalIDs) - : this(seasonNumber, episodeNumber, absoluteNumber, title, overview, releaseDate, runtime, poster, externalIDs) - { - ShowID = showID; - SeasonID = seasonID; - Path = path; - } - public static string GetSlug(string showSlug, int seasonNumber, int episodeNumber, int absoluteNumber) { if (showSlug == null) diff --git a/Kyoo.Common/Models/Resources/Library.cs b/Kyoo.Common/Models/Resources/Library.cs index 86cb324d..c8148544 100644 --- a/Kyoo.Common/Models/Resources/Library.cs +++ b/Kyoo.Common/Models/Resources/Library.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Kyoo.Models.Attributes; namespace Kyoo.Models @@ -21,15 +20,5 @@ namespace Kyoo.Models [SerializeIgnore] public virtual ICollection> ShowLinks { get; set; } [SerializeIgnore] public virtual ICollection> CollectionLinks { get; set; } #endif - - public Library() { } - - public Library(string slug, string name, IEnumerable paths, IEnumerable providers) - { - Slug = slug; - Name = name; - Paths = paths?.ToArray(); - Providers = providers?.ToArray(); - } } } diff --git a/Kyoo.Common/Models/Resources/People.cs b/Kyoo.Common/Models/Resources/People.cs index 2743cf3c..46b86143 100644 --- a/Kyoo.Common/Models/Resources/People.cs +++ b/Kyoo.Common/Models/Resources/People.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Kyoo.Models.Attributes; namespace Kyoo.Models @@ -13,15 +12,5 @@ namespace Kyoo.Models [EditableRelation] [LoadableRelation] public virtual ICollection ExternalIDs { get; set; } [EditableRelation] [LoadableRelation] public virtual ICollection Roles { get; set; } - - public People() {} - - public People(string slug, string name, string poster, IEnumerable externalIDs) - { - Slug = slug; - Name = name; - Poster = poster; - ExternalIDs = externalIDs?.ToArray(); - } } } diff --git a/Kyoo.Common/Models/Resources/Season.cs b/Kyoo.Common/Models/Resources/Season.cs index 827b24f7..b3f7ab27 100644 --- a/Kyoo.Common/Models/Resources/Season.cs +++ b/Kyoo.Common/Models/Resources/Season.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Kyoo.Models.Attributes; namespace Kyoo.Models @@ -22,24 +21,5 @@ namespace Kyoo.Models [EditableRelation] [LoadableRelation] public virtual ICollection ExternalIDs { get; set; } [LoadableRelation] public virtual ICollection Episodes { get; set; } - - public Season() { } - - public Season(int showID, - int seasonNumber, - string title, - string overview, - int? year, - string poster, - IEnumerable externalIDs) - { - ShowID = showID; - SeasonNumber = seasonNumber; - Title = title; - Overview = overview; - Year = year; - Poster = poster; - ExternalIDs = externalIDs?.ToArray(); - } } } diff --git a/Kyoo.Common/Models/Resources/Show.cs b/Kyoo.Common/Models/Resources/Show.cs index 7b948b55..e7d14d79 100644 --- a/Kyoo.Common/Models/Resources/Show.cs +++ b/Kyoo.Common/Models/Resources/Show.cs @@ -42,62 +42,6 @@ namespace Kyoo.Models [SerializeIgnore] public virtual ICollection> GenreLinks { get; set; } #endif - - public Show() { } - - public Show(string slug, - string title, - IEnumerable aliases, - string path, string overview, - string trailerUrl, - IEnumerable genres, - Status? status, - int? startYear, - int? endYear, - IEnumerable externalIDs) - { - Slug = slug; - Title = title; - Aliases = aliases?.ToArray(); - Path = path; - Overview = overview; - TrailerUrl = trailerUrl; - Genres = genres?.ToArray(); - Status = status; - StartYear = startYear; - EndYear = endYear; - ExternalIDs = externalIDs?.ToArray(); - } - - public Show(string slug, - string title, - IEnumerable aliases, - string path, - string overview, - string trailerUrl, - Status? status, - int? startYear, - int? endYear, - string poster, - string logo, - string backdrop, - IEnumerable externalIDs) - { - Slug = slug; - Title = title; - Aliases = aliases?.ToArray(); - Path = path; - Overview = overview; - TrailerUrl = trailerUrl; - Status = status; - StartYear = startYear; - EndYear = endYear; - Poster = poster; - Logo = logo; - Backdrop = backdrop; - ExternalIDs = externalIDs?.ToArray(); - } - public string GetID(string provider) { return ExternalIDs?.FirstOrDefault(x => x.Provider.Name == provider)?.DataID; diff --git a/Kyoo.CommonAPI/CrudApi.cs b/Kyoo.CommonAPI/CrudApi.cs index 27ddf66b..f14f10db 100644 --- a/Kyoo.CommonAPI/CrudApi.cs +++ b/Kyoo.CommonAPI/CrudApi.cs @@ -29,28 +29,20 @@ namespace Kyoo.CommonApi [Authorize(Policy = "Read")] public virtual async Task> Get(int id) { - try - { - return await _repository.Get(id); - } - catch (ItemNotFoundException) - { + T ret = await _repository.GetOrDefault(id); + if (ret == null) return NotFound(); - } + return ret; } [HttpGet("{slug}")] [Authorize(Policy = "Read")] public virtual async Task> Get(string slug) { - try - { - return await _repository.Get(slug); - } - catch (ItemNotFoundException) - { + T ret = await _repository.Get(slug); + if (ret == null) return NotFound(); - } + return ret; } [HttpGet("count")] @@ -111,7 +103,7 @@ namespace Kyoo.CommonApi } catch (DuplicatedItemException) { - T existing = await _repository.Get(resource.Slug); + T existing = await _repository.GetOrDefault(resource.Slug); return Conflict(existing); } } diff --git a/Kyoo.CommonAPI/DatabaseContext.cs b/Kyoo.CommonAPI/DatabaseContext.cs index 6b1bac47..584230b9 100644 --- a/Kyoo.CommonAPI/DatabaseContext.cs +++ b/Kyoo.CommonAPI/DatabaseContext.cs @@ -8,7 +8,6 @@ using Kyoo.Models; using Kyoo.Models.Exceptions; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; -using Microsoft.EntityFrameworkCore.Diagnostics; namespace Kyoo { diff --git a/Kyoo.CommonAPI/LocalRepository.cs b/Kyoo.CommonAPI/LocalRepository.cs index ee01b089..7498fc14 100644 --- a/Kyoo.CommonAPI/LocalRepository.cs +++ b/Kyoo.CommonAPI/LocalRepository.cs @@ -125,7 +125,7 @@ namespace Kyoo.Controllers Sort sort = default, Pagination limit = default) { - return ApplyFilters(query, Get, DefaultSort, where, sort, limit); + return ApplyFilters(query, GetOrDefault, DefaultSort, where, sort, limit); } /// @@ -193,14 +193,14 @@ namespace Kyoo.Controllers } /// - public virtual async Task CreateIfNotExists(T obj, bool silentFail = false) + public virtual async Task CreateIfNotExists(T obj) { try { if (obj == null) throw new ArgumentNullException(nameof(obj)); - T old = await Get(obj.Slug); + T old = await GetOrDefault(obj.Slug); if (old != null) return old; @@ -208,13 +208,7 @@ namespace Kyoo.Controllers } catch (DuplicatedItemException) { - return await Get(obj.Slug); - } - catch - { - if (silentFail) - return default; - throw; + return await GetOrDefault(obj.Slug); } } diff --git a/Kyoo.Postgresql/PostgresModule.cs b/Kyoo.Postgresql/PostgresModule.cs index 506f6dbe..7a818296 100644 --- a/Kyoo.Postgresql/PostgresModule.cs +++ b/Kyoo.Postgresql/PostgresModule.cs @@ -63,7 +63,7 @@ namespace Kyoo.Postgresql services.AddDbContext(x => { x.UseNpgsql(_configuration.GetDatabaseConnection("postgres")); - if (_configuration.GetValue("logging:dotnet-ef")) + if (_environment.IsDevelopment()) x.EnableDetailedErrors().EnableSensitiveDataLogging(); }); // services.AddScoped(_ => new PostgresContext( diff --git a/Kyoo.Tests/Library/SetupTests.cs b/Kyoo.Tests/Library/SetupTests.cs index e1852a17..ec9ed12a 100644 --- a/Kyoo.Tests/Library/SetupTests.cs +++ b/Kyoo.Tests/Library/SetupTests.cs @@ -1,6 +1,3 @@ -using System.Linq; -using Xunit; - namespace Kyoo.Tests { public class SetupTests diff --git a/Kyoo.WebApp b/Kyoo.WebApp index 6802bc11..d3a860fa 160000 --- a/Kyoo.WebApp +++ b/Kyoo.WebApp @@ -1 +1 @@ -Subproject commit 6802bc11e66331f0e77d7604838c8f1c219bef99 +Subproject commit d3a860fa8ffccade9e3b17022482e11c9a18303e diff --git a/Kyoo/Controllers/Repositories/EpisodeRepository.cs b/Kyoo/Controllers/Repositories/EpisodeRepository.cs index 91e39044..8452b950 100644 --- a/Kyoo/Controllers/Repositories/EpisodeRepository.cs +++ b/Kyoo/Controllers/Repositories/EpisodeRepository.cs @@ -234,7 +234,7 @@ namespace Kyoo.Controllers await base.Validate(resource); resource.ExternalIDs = await resource.ExternalIDs.SelectAsync(async x => { - x.Provider = await _providers.CreateIfNotExists(x.Provider, true); + x.Provider = await _providers.CreateIfNotExists(x.Provider); x.ProviderID = x.Provider.ID; _database.Entry(x.Provider).State = EntityState.Detached; return x; diff --git a/Kyoo/Controllers/Repositories/LibraryItemRepository.cs b/Kyoo/Controllers/Repositories/LibraryItemRepository.cs index ab872a50..703ece0b 100644 --- a/Kyoo/Controllers/Repositories/LibraryItemRepository.cs +++ b/Kyoo/Controllers/Repositories/LibraryItemRepository.cs @@ -111,12 +111,7 @@ namespace Kyoo.Controllers public override Task Create(LibraryItem obj) => throw new InvalidOperationException(); /// - public override Task CreateIfNotExists(LibraryItem obj, bool silentFail = false) - { - if (silentFail) - return Task.FromResult(default); - throw new InvalidOperationException(); - } + public override Task CreateIfNotExists(LibraryItem obj) => throw new InvalidOperationException(); /// public override Task Edit(LibraryItem obj, bool reset) => throw new InvalidOperationException(); /// diff --git a/Kyoo/Controllers/Repositories/LibraryRepository.cs b/Kyoo/Controllers/Repositories/LibraryRepository.cs index affd899c..d569f6fe 100644 --- a/Kyoo/Controllers/Repositories/LibraryRepository.cs +++ b/Kyoo/Controllers/Repositories/LibraryRepository.cs @@ -53,9 +53,8 @@ namespace Kyoo.Controllers public override async Task Create(Library obj) { await base.Create(obj); + obj.ProviderLinks = obj.Providers?.Select(x => Link.Create(obj, x)).ToList(); _database.Entry(obj).State = EntityState.Added; - obj.ProviderLinks = obj.Providers?.Select(x => Link.Create(obj, x)).ToArray(); - obj.ProviderLinks.ForEach(x => _database.Entry(x).State = EntityState.Added); await _database.SaveChangesAsync($"Trying to insert a duplicated library (slug {obj.Slug} already exists)."); return obj; } @@ -65,7 +64,7 @@ namespace Kyoo.Controllers { await base.Validate(resource); resource.Providers = await resource.Providers - .SelectAsync(x => _providers.CreateIfNotExists(x, true)) + .SelectAsync(x => _providers.CreateIfNotExists(x)) .ToListAsync(); } diff --git a/Kyoo/Controllers/Repositories/PeopleRepository.cs b/Kyoo/Controllers/Repositories/PeopleRepository.cs index 2c9c6156..452f59eb 100644 --- a/Kyoo/Controllers/Repositories/PeopleRepository.cs +++ b/Kyoo/Controllers/Repositories/PeopleRepository.cs @@ -73,13 +73,13 @@ namespace Kyoo.Controllers await base.Validate(resource); await resource.ExternalIDs.ForEachAsync(async id => { - id.Provider = await _providers.CreateIfNotExists(id.Provider, true); + id.Provider = await _providers.CreateIfNotExists(id.Provider); id.ProviderID = id.Provider.ID; _database.Entry(id.Provider).State = EntityState.Detached; }); await resource.Roles.ForEachAsync(async role => { - role.Show = await _shows.Value.CreateIfNotExists(role.Show, true); + role.Show = await _shows.Value.CreateIfNotExists(role.Show); role.ShowID = role.Show.ID; _database.Entry(role.Show).State = EntityState.Detached; }); @@ -129,7 +129,7 @@ namespace Kyoo.Controllers where, sort, limit); - if (!people.Any() && await _shows.Value.Get(showID) == null) + if (!people.Any() && await _shows.Value.GetOrDefault(showID) == null) throw new ItemNotFoundException(); foreach (PeopleRole role in people) role.ForPeople = true; @@ -151,7 +151,7 @@ namespace Kyoo.Controllers where, sort, limit); - if (!people.Any() && await _shows.Value.Get(showSlug) == null) + if (!people.Any() && await _shows.Value.GetOrDefault(showSlug) == null) throw new ItemNotFoundException(); foreach (PeopleRole role in people) role.ForPeople = true; @@ -172,7 +172,7 @@ namespace Kyoo.Controllers where, sort, limit); - if (!roles.Any() && await Get(id) == null) + if (!roles.Any() && await GetOrDefault(id) == null) throw new ItemNotFoundException(); return roles; } @@ -191,7 +191,7 @@ namespace Kyoo.Controllers where, sort, limit); - if (!roles.Any() && await Get(slug) == null) + if (!roles.Any() && await GetOrDefault(slug) == null) throw new ItemNotFoundException(); return roles; } diff --git a/Kyoo/Controllers/Repositories/SeasonRepository.cs b/Kyoo/Controllers/Repositories/SeasonRepository.cs index a6ef7fcd..289ca08f 100644 --- a/Kyoo/Controllers/Repositories/SeasonRepository.cs +++ b/Kyoo/Controllers/Repositories/SeasonRepository.cs @@ -160,7 +160,7 @@ namespace Kyoo.Controllers await base.Validate(resource); await resource.ExternalIDs.ForEachAsync(async id => { - id.Provider = await _providers.CreateIfNotExists(id.Provider, true); + id.Provider = await _providers.CreateIfNotExists(id.Provider); id.ProviderID = id.Provider.ID; _database.Entry(id.Provider).State = EntityState.Detached; }); diff --git a/Kyoo/Controllers/Repositories/ShowRepository.cs b/Kyoo/Controllers/Repositories/ShowRepository.cs index 2498e607..eb3f36e4 100644 --- a/Kyoo/Controllers/Repositories/ShowRepository.cs +++ b/Kyoo/Controllers/Repositories/ShowRepository.cs @@ -102,22 +102,22 @@ namespace Kyoo.Controllers { await base.Validate(resource); if (resource.Studio != null) - resource.Studio = await _studios.CreateIfNotExists(resource.Studio, true); + resource.Studio = await _studios.CreateIfNotExists(resource.Studio); resource.Genres = await resource.Genres - .SelectAsync(x => _genres.CreateIfNotExists(x, true)) + .SelectAsync(x => _genres.CreateIfNotExists(x)) .ToListAsync(); resource.GenreLinks = resource.Genres? .Select(x => Link.UCreate(resource, x)) .ToList(); await resource.ExternalIDs.ForEachAsync(async id => { - id.Provider = await _providers.CreateIfNotExists(id.Provider, true); + id.Provider = await _providers.CreateIfNotExists(id.Provider); id.ProviderID = id.Provider.ID; _database.Entry(id.Provider).State = EntityState.Detached; }); await resource.People.ForEachAsync(async role => { - role.People = await _people.CreateIfNotExists(role.People, true); + role.People = await _people.CreateIfNotExists(role.People); role.PeopleID = role.People.ID; _database.Entry(role.People).State = EntityState.Detached; }); diff --git a/Kyoo/Controllers/Repositories/TrackRepository.cs b/Kyoo/Controllers/Repositories/TrackRepository.cs index 4ce6da29..4b27a5e4 100644 --- a/Kyoo/Controllers/Repositories/TrackRepository.cs +++ b/Kyoo/Controllers/Repositories/TrackRepository.cs @@ -59,7 +59,7 @@ namespace Kyoo.Controllers if (!match.Success) { if (int.TryParse(slug, out int id)) - return Get(id); + return GetOrDefault(id); match = Regex.Match(slug, @"(?.*)\.(?.{0,3})(?-forced)?(\..*)?"); if (!match.Success) throw new ArgumentException("Invalid track slug. " + @@ -102,6 +102,7 @@ namespace Kyoo.Controllers await base.Create(obj); _database.Entry(obj).State = EntityState.Added; + // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local await _database.SaveOrRetry(obj, (x, i) => { if (i > 10) diff --git a/Kyoo/Program.cs b/Kyoo/Program.cs index 34ae9206..9fecc61b 100644 --- a/Kyoo/Program.cs +++ b/Kyoo/Program.cs @@ -83,7 +83,10 @@ namespace Kyoo .ConfigureLogging((context, builder) => { builder.AddConfiguration(context.Configuration.GetSection("logging")) - .AddConsole() + .AddSimpleConsole(x => + { + x.TimestampFormat = "[hh:mm:ss] "; + }) .AddDebug() .AddEventSourceLogger(); }) diff --git a/Kyoo/Startup.cs b/Kyoo/Startup.cs index 0054c066..a612f7d5 100644 --- a/Kyoo/Startup.cs +++ b/Kyoo/Startup.cs @@ -47,7 +47,8 @@ namespace Kyoo _configuration = configuration; _plugins = new PluginManager(hostProvider, _configuration, loggerFactory.CreateLogger()); - _plugins.LoadPlugins(new IPlugin[] {new CoreModule(), new PostgresModule(configuration, host), new AuthenticationModule(configuration, loggerFactory)}); + _plugins.LoadPlugins(new IPlugin[] {new CoreModule(), new PostgresModule(configuration, host), + new AuthenticationModule(configuration, loggerFactory, host)}); } /// diff --git a/Kyoo/Tasks/Crawler.cs b/Kyoo/Tasks/Crawler.cs index 32ea147c..e6a6cebf 100644 --- a/Kyoo/Tasks/Crawler.cs +++ b/Kyoo/Tasks/Crawler.cs @@ -73,7 +73,7 @@ namespace Kyoo.Tasks ICollection libraries = argument == null ? await libraryManager.GetAll() - : new [] { await libraryManager.Get(argument)}; + : new [] { await libraryManager.GetOrDefault(argument)}; if (argument != null && libraries.First() == null) throw new ArgumentException($"No library found with the name {argument}"); @@ -253,7 +253,7 @@ namespace Kyoo.Tasks { if (string.IsNullOrEmpty(collectionName)) return null; - Collection collection = await libraryManager.Get(Utility.ToSlug(collectionName)); + Collection collection = await libraryManager.GetOrDefault(Utility.ToSlug(collectionName)); if (collection != null) return collection; collection = await MetadataProvider.GetCollectionFromName(collectionName, library); @@ -265,7 +265,7 @@ namespace Kyoo.Tasks } catch (DuplicatedItemException) { - return await libraryManager.Get(collection.Slug); + return await libraryManager.GetOrDefault(collection.Slug); } } @@ -275,7 +275,7 @@ namespace Kyoo.Tasks bool isMovie, Library library) { - Show old = await libraryManager.Get(x => x.Path == showPath); + Show old = await libraryManager.GetOrDefault(x => x.Path == showPath); if (old != null) { await libraryManager.Load(old, x => x.ExternalIDs); @@ -291,7 +291,7 @@ namespace Kyoo.Tasks } catch (DuplicatedItemException) { - old = await libraryManager.Get(show.Slug); + old = await libraryManager.GetOrDefault(show.Slug); if (old.Path == showPath) { await libraryManager.Load(old, x => x.ExternalIDs); @@ -320,8 +320,15 @@ namespace Kyoo.Tasks catch (ItemNotFoundException) { Season season = await MetadataProvider.GetSeason(show, seasonNumber, library); - await libraryManager.CreateIfNotExists(season); - await ThumbnailsManager.Validate(season); + try + { + await libraryManager.Create(season); + await ThumbnailsManager.Validate(season); + } + catch (DuplicatedItemException) + { + season = await libraryManager.Get(show.Slug, seasonNumber); + } season.Show = show; return season; } diff --git a/Kyoo/Views/CollectionApi.cs b/Kyoo/Views/CollectionApi.cs index 0d5b0be5..92bcc406 100644 --- a/Kyoo/Views/CollectionApi.cs +++ b/Kyoo/Views/CollectionApi.cs @@ -40,7 +40,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(id) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(id) == null) return NotFound(); return Page(resources, limit); } @@ -92,7 +92,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(id) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(id) == null) return NotFound(); return Page(resources, limit); } diff --git a/Kyoo/Views/EpisodeApi.cs b/Kyoo/Views/EpisodeApi.cs index a77f408e..4935326a 100644 --- a/Kyoo/Views/EpisodeApi.cs +++ b/Kyoo/Views/EpisodeApi.cs @@ -50,14 +50,20 @@ namespace Kyoo.Api [Authorize(Policy = "Read")] public async Task> GetShow(int showID, int seasonNumber, int episodeNumber) { - return await _libraryManager.Get(showID); + Show ret = await _libraryManager.GetOrDefault(showID); + if (ret == null) + return NotFound(); + return ret; } [HttpGet("{episodeID:int}/season")] [Authorize(Policy = "Read")] public async Task> GetSeason(int episodeID) { - return await _libraryManager.Get(x => x.Episodes.Any(y => y.ID == episodeID)); + Season ret = await _libraryManager.GetOrDefault(x => x.Episodes.Any(y => y.ID == episodeID)); + if (ret == null) + return NotFound(); + return ret; } [HttpGet("{showSlug}-s{seasonNumber:int}e{episodeNumber:int}/season")] @@ -104,7 +110,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(episodeID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(episodeID) == null) return NotFound(); return Page(resources, limit); } @@ -175,7 +181,6 @@ namespace Kyoo.Api } [HttpGet("{id:int}/thumb")] - [Authorize(Policy="Read")] public async Task GetThumb(int id) { try @@ -190,7 +195,6 @@ namespace Kyoo.Api } [HttpGet("{slug}/thumb")] - [Authorize(Policy="Read")] public async Task GetThumb(string slug) { try diff --git a/Kyoo/Views/GenreApi.cs b/Kyoo/Views/GenreApi.cs index ab40f20b..c2df6b13 100644 --- a/Kyoo/Views/GenreApi.cs +++ b/Kyoo/Views/GenreApi.cs @@ -5,7 +5,6 @@ using System.Threading.Tasks; using Kyoo.CommonApi; using Kyoo.Controllers; using Kyoo.Models; -using Kyoo.Models.Exceptions; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -41,7 +40,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(id) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(id) == null) return NotFound(); return Page(resources, limit); } diff --git a/Kyoo/Views/LibraryApi.cs b/Kyoo/Views/LibraryApi.cs index 4ec44ea1..46707eec 100644 --- a/Kyoo/Views/LibraryApi.cs +++ b/Kyoo/Views/LibraryApi.cs @@ -6,7 +6,6 @@ using Kyoo.Models; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using Kyoo.CommonApi; -using Kyoo.Models.Exceptions; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.Configuration; @@ -27,7 +26,7 @@ namespace Kyoo.Api _taskManager = taskManager; } - [Authorize(Policy = "Admin")] + [Authorize(Policy = "Write")] public override async Task> Create(Library resource) { ActionResult result = await base.Create(resource); @@ -52,7 +51,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(id) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(id) == null) return NotFound(); return Page(resources, limit); } @@ -104,7 +103,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(id) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(id) == null) return NotFound(); return Page(resources, limit); } @@ -156,7 +155,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(id) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(id) == null) return NotFound(); return Page(resources, limit); } diff --git a/Kyoo/Views/PeopleApi.cs b/Kyoo/Views/PeopleApi.cs index ed68dea3..001caa33 100644 --- a/Kyoo/Views/PeopleApi.cs +++ b/Kyoo/Views/PeopleApi.cs @@ -87,18 +87,20 @@ namespace Kyoo.Api } [HttpGet("{id:int}/poster")] - [Authorize(Policy="Read")] public async Task GetPeopleIcon(int id) { - People people = await _libraryManager.Get(id); + People people = await _libraryManager.GetOrDefault(id); + if (people == null) + return NotFound(); return _files.FileResult(await _thumbs.GetPeoplePoster(people)); } [HttpGet("{slug}/poster")] - [Authorize(Policy="Read")] public async Task GetPeopleIcon(string slug) { - People people = await _libraryManager.Get(slug); + People people = await _libraryManager.GetOrDefault(slug); + if (people == null) + return NotFound(); return _files.FileResult(await _thumbs.GetPeoplePoster(people)); } } diff --git a/Kyoo/Views/ProviderApi.cs b/Kyoo/Views/ProviderApi.cs index 050f2681..20d08f7b 100644 --- a/Kyoo/Views/ProviderApi.cs +++ b/Kyoo/Views/ProviderApi.cs @@ -2,7 +2,6 @@ using System.Threading.Tasks; using Kyoo.CommonApi; using Kyoo.Controllers; using Kyoo.Models; -using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; @@ -29,18 +28,20 @@ namespace Kyoo.Api } [HttpGet("{id:int}/logo")] - [Authorize(Policy="Read")] public async Task GetLogo(int id) { - Provider provider = await _libraryManager.Get(id); + Provider provider = await _libraryManager.GetOrDefault(id); + if (provider == null) + return NotFound(); return _files.FileResult(await _thumbnails.GetProviderLogo(provider)); } [HttpGet("{slug}/logo")] - [Authorize(Policy="Read")] public async Task GetLogo(string slug) { - Provider provider = await _libraryManager.Get(slug); + Provider provider = await _libraryManager.GetOrDefault(slug); + if (provider == null) + return NotFound(); return _files.FileResult(await _thumbnails.GetProviderLogo(provider)); } } diff --git a/Kyoo/Views/SeasonApi.cs b/Kyoo/Views/SeasonApi.cs index 9803f956..b578f350 100644 --- a/Kyoo/Views/SeasonApi.cs +++ b/Kyoo/Views/SeasonApi.cs @@ -47,7 +47,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(seasonID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(seasonID) == null) return NotFound(); return Page(resources, limit); } @@ -130,23 +130,28 @@ namespace Kyoo.Api [Authorize(Policy = "Read")] public async Task> GetShow(int showID, int seasonNumber) { - return await _libraryManager.Get(showID); + Show ret = await _libraryManager.GetOrDefault(showID); + if (ret == null) + return NotFound(); + return ret; } [HttpGet("{id:int}/thumb")] - [Authorize(Policy="Read")] public async Task GetThumb(int id) { - Season season = await _libraryManager.Get(id); + Season season = await _libraryManager.GetOrDefault(id); + if (season == null) + return NotFound(); await _libraryManager.Load(season, x => x.Show); return _files.FileResult(await _thumbs.GetSeasonPoster(season)); } [HttpGet("{slug}/thumb")] - [Authorize(Policy="Read")] public async Task GetThumb(string slug) { - Season season = await _libraryManager.Get(slug); + Season season = await _libraryManager.GetOrDefault(slug); + if (season == null) + return NotFound(); await _libraryManager.Load(season, x => x.Show); return _files.FileResult(await _thumbs.GetSeasonPoster(season)); } diff --git a/Kyoo/Views/ShowApi.cs b/Kyoo/Views/ShowApi.cs index 7b2a8868..b1d17538 100644 --- a/Kyoo/Views/ShowApi.cs +++ b/Kyoo/Views/ShowApi.cs @@ -49,7 +49,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(showID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(showID) == null) return NotFound(); return Page(resources, limit); } @@ -75,7 +75,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } @@ -101,7 +101,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(showID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(showID) == null) return NotFound(); return Page(resources, limit); } @@ -127,7 +127,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } @@ -152,7 +152,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(showID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(showID) == null) return NotFound(); return Page(resources, limit); } @@ -177,7 +177,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } @@ -203,7 +203,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(showID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(showID) == null) return NotFound(); return Page(resources, limit); } @@ -229,7 +229,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } @@ -283,7 +283,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(showID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(showID) == null) return NotFound(); return Page(resources, limit); } @@ -309,7 +309,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } @@ -335,7 +335,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(showID) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(showID) == null) return NotFound(); return Page(resources, limit); } @@ -361,7 +361,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } @@ -408,7 +408,6 @@ namespace Kyoo.Api } [HttpGet("{slug}/poster")] - [Authorize(Policy = "Read")] public async Task GetPoster(string slug) { try @@ -423,7 +422,6 @@ namespace Kyoo.Api } [HttpGet("{slug}/logo")] - [Authorize(Policy="Read")] public async Task GetLogo(string slug) { try @@ -438,7 +436,6 @@ namespace Kyoo.Api } [HttpGet("{slug}/backdrop")] - [Authorize(Policy="Read")] public async Task GetBackdrop(string slug) { try diff --git a/Kyoo/Views/StudioApi.cs b/Kyoo/Views/StudioApi.cs index 45f46829..ddf86092 100644 --- a/Kyoo/Views/StudioApi.cs +++ b/Kyoo/Views/StudioApi.cs @@ -40,7 +40,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(id) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(id) == null) return NotFound(); return Page(resources, limit); } @@ -66,7 +66,7 @@ namespace Kyoo.Api new Sort(sortBy), new Pagination(limit, afterID)); - if (!resources.Any() && await _libraryManager.Get(slug) == null) + if (!resources.Any() && await _libraryManager.GetOrDefault(slug) == null) return NotFound(); return Page(resources, limit); } diff --git a/Kyoo/settings.json b/Kyoo/settings.json index cc213cc0..ff3fffd3 100644 --- a/Kyoo/settings.json +++ b/Kyoo/settings.json @@ -20,12 +20,9 @@ "default": "Trace", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information", - "Microsoft.EntityFrameworkCore.DbUpdateException": "None", - "Microsoft.EntityFrameworkCore.Update": "None", - "Microsoft.EntityFrameworkCore.Database.Command": "None", + "Microsoft.EntityFrameworkCore": "None", "Kyoo": "Trace" - }, - "dotnet-ef": "false" + } }, "authentication": { @@ -35,7 +32,7 @@ "password": "passphrase" }, "permissions": { - "default": ["read", "play", "write", "admin"], + "default": [], "newUser": ["read", "play", "write", "admin"] }, "profilePicturePath": "users/"