From aec2a8f51a711dbc31800ae0f1992b5a08df5ae9 Mon Sep 17 00:00:00 2001 From: Zoe Roux Date: Fri, 3 Sep 2021 22:28:35 +0200 Subject: [PATCH] CodingStyle: Fixing more issues --- .../Controllers/ILibraryManager.cs | 109 ++++++----- .../Controllers/IMetadataProvider.cs | 8 +- .../Controllers/IPermissionValidator.cs | 29 +++ Kyoo.Abstractions/Controllers/IPlugin.cs | 2 +- Kyoo.Abstractions/Controllers/IRepository.cs | 7 +- .../Permission/PartialPermissionAttribute.cs | 70 +++++++ .../Permission/PermissionAttribute.cs | 110 +++++++++++ .../Models/Attributes/PermissionAttribute.cs | 172 ------------------ Kyoo.Abstractions/Models/Resources/Show.cs | 34 +++- Kyoo.Authentication/AuthenticationModule.cs | 2 +- ...ionValidator.cs => PermissionValidator.cs} | 43 +++-- .../PassthroughPermissionValidator.cs | 9 +- Kyoo.Core/Views/VideoApi.cs | 4 +- Kyoo.Database/DatabaseContext.cs | 21 ++- ...{ProviderTmdb.cs => TheMovieDbProvider.cs} | 28 +-- Kyoo.ruleset | 12 ++ 16 files changed, 390 insertions(+), 270 deletions(-) create mode 100644 Kyoo.Abstractions/Controllers/IPermissionValidator.cs create mode 100644 Kyoo.Abstractions/Models/Attributes/Permission/PartialPermissionAttribute.cs create mode 100644 Kyoo.Abstractions/Models/Attributes/Permission/PermissionAttribute.cs delete mode 100644 Kyoo.Abstractions/Models/Attributes/PermissionAttribute.cs rename Kyoo.Authentication/Controllers/{PremissionValidator.cs => PermissionValidator.cs} (77%) rename Kyoo.TheMovieDb/{ProviderTmdb.cs => TheMovieDbProvider.cs} (91%) diff --git a/Kyoo.Abstractions/Controllers/ILibraryManager.cs b/Kyoo.Abstractions/Controllers/ILibraryManager.cs index 2257af97..8b63bdb9 100644 --- a/Kyoo.Abstractions/Controllers/ILibraryManager.cs +++ b/Kyoo.Abstractions/Controllers/ILibraryManager.cs @@ -10,7 +10,7 @@ using Kyoo.Abstractions.Models.Exceptions; namespace Kyoo.Abstractions.Controllers { /// - /// An interface to interract with the database. Every repository is mapped through here. + /// An interface to interact with the database. Every repository is mapped through here. /// public interface ILibraryManager { @@ -20,7 +20,8 @@ namespace Kyoo.Abstractions.Controllers /// The type you want /// If the item is not found /// The repository corresponding - IRepository GetRepository() where T : class, IResource; + IRepository GetRepository() + where T : class, IResource; /// /// The repository that handle libraries. @@ -28,7 +29,7 @@ namespace Kyoo.Abstractions.Controllers ILibraryRepository LibraryRepository { get; } /// - /// The repository that handle libraries's items (a wrapper arround shows & collections). + /// The repository that handle libraries items (a wrapper around shows & collections). /// ILibraryItemRepository LibraryItemRepository { get; } @@ -90,7 +91,8 @@ namespace Kyoo.Abstractions.Controllers /// If the item is not found /// The resource found [ItemNotNull] - Task Get(int id) where T : class, IResource; + Task Get(int id) + where T : class, IResource; /// /// Get the resource by it's slug @@ -100,7 +102,8 @@ namespace Kyoo.Abstractions.Controllers /// If the item is not found /// The resource found [ItemNotNull] - Task Get(string slug) where T : class, IResource; + Task Get(string slug) + where T : class, IResource; /// /// Get the resource by a filter function. @@ -110,7 +113,8 @@ namespace Kyoo.Abstractions.Controllers /// If the item is not found /// The first resource found that match the where function [ItemNotNull] - Task Get(Expression> where) where T : class, IResource; + Task Get(Expression> where) + where T : class, IResource; /// /// Get a season from it's showID and it's seasonNumber @@ -161,7 +165,8 @@ namespace Kyoo.Abstractions.Controllers /// The type of the resource /// The resource found [ItemCanBeNull] - Task GetOrDefault(int id) where T : class, IResource; + Task GetOrDefault(int id) + where T : class, IResource; /// /// Get the resource by it's slug or null if it is not found. @@ -170,7 +175,8 @@ namespace Kyoo.Abstractions.Controllers /// The type of the resource /// The resource found [ItemCanBeNull] - Task GetOrDefault(string slug) where T : class, IResource; + Task GetOrDefault(string slug) + where T : class, IResource; /// /// Get the resource by a filter function or null if it is not found. @@ -179,7 +185,8 @@ namespace Kyoo.Abstractions.Controllers /// The type of the resource /// The first resource found that match the where function [ItemCanBeNull] - Task GetOrDefault(Expression> where) where T : class, IResource; + Task GetOrDefault(Expression> where) + where T : class, IResource; /// /// Get a season from it's showID and it's seasonNumber or null if it is not found. @@ -219,20 +226,19 @@ namespace Kyoo.Abstractions.Controllers [ItemCanBeNull] Task GetOrDefault(string showSlug, int seasonNumber, int episodeNumber); - /// /// Load a related resource /// /// The source object. /// A getter function for the member to load /// - /// true if you want to load the relation even if it is not null, false otherwise. + /// true if you want to load the relation even if it is not null, false otherwise. /// /// The type of the source object /// The related resource's type /// The param /// - /// + /// /// Task Load([NotNull] T obj, Expression> member, bool force = false) where T : class, IResource @@ -244,13 +250,13 @@ namespace Kyoo.Abstractions.Controllers /// The source object. /// A getter function for the member to load /// - /// true if you want to load the relation even if it is not null, false otherwise. + /// true if you want to load the relation even if it is not null, false otherwise. /// /// The type of the source object /// The related resource's type /// The param /// - /// + /// /// Task Load([NotNull] T obj, Expression>> member, bool force = false) where T : class, IResource @@ -262,7 +268,7 @@ namespace Kyoo.Abstractions.Controllers /// The source object. /// The name of the resource to load (case sensitive) /// - /// true if you want to load the relation even if it is not null, false otherwise. + /// true if you want to load the relation even if it is not null, false otherwise. /// /// The type of the source object /// The param @@ -273,24 +279,25 @@ namespace Kyoo.Abstractions.Controllers where T : class, IResource; /// - /// Load a related resource without specifing it's type. + /// Load a related resource without specifying it's type. /// /// The source object. /// The name of the resource to load (case sensitive) /// - /// true if you want to load the relation even if it is not null, false otherwise. + /// true if you want to load the relation even if it is not null, false otherwise. /// /// /// - /// + /// + /// A representing the asynchronous operation. Task Load([NotNull] IResource obj, string memberName, bool force = false); /// - /// Get items (A wrapper arround shows or collections) from a library. + /// Get items (A wrapper around shows or collections) from a library. /// /// The ID of the library /// A filter function - /// Sort informations (sort order & sort by) + /// Sort information (sort order & sort by) /// How many items to return and where to start /// A list of items that match every filters Task> GetItemsFromLibrary(int id, @@ -299,7 +306,7 @@ namespace Kyoo.Abstractions.Controllers Pagination limit = default); /// - /// Get items (A wrapper arround shows or collections) from a library. + /// Get items (A wrapper around shows or collections) from a library. /// /// The ID of the library /// A filter function @@ -313,11 +320,11 @@ namespace Kyoo.Abstractions.Controllers ) => GetItemsFromLibrary(id, where, new Sort(sort), limit); /// - /// Get items (A wrapper arround shows or collections) from a library. + /// Get items (A wrapper around shows or collections) from a library. /// /// The slug of the library /// A filter function - /// Sort informations (sort order & sort by) + /// Sort information (sort order & sort by) /// How many items to return and where to start /// A list of items that match every filters Task> GetItemsFromLibrary(string slug, @@ -326,7 +333,7 @@ namespace Kyoo.Abstractions.Controllers Pagination limit = default); /// - /// Get items (A wrapper arround shows or collections) from a library. + /// Get items (A wrapper around shows or collections) from a library. /// /// The slug of the library /// A filter function @@ -339,13 +346,12 @@ namespace Kyoo.Abstractions.Controllers Pagination limit = default ) => GetItemsFromLibrary(slug, where, new Sort(sort), limit); - /// /// Get people's roles from a show. /// /// The ID of the show /// A filter function - /// Sort informations (sort order & sort by) + /// Sort information (sort order & sort by) /// How many items to return and where to start /// A list of items that match every filters Task> GetPeopleFromShow(int showID, @@ -372,7 +378,7 @@ namespace Kyoo.Abstractions.Controllers /// /// The slug of the show /// A filter function - /// Sort informations (sort order & sort by) + /// Sort information (sort order & sort by) /// How many items to return and where to start /// A list of items that match every filters Task> GetPeopleFromShow(string showSlug, @@ -394,13 +400,12 @@ namespace Kyoo.Abstractions.Controllers Pagination limit = default ) => GetPeopleFromShow(showSlug, where, new Sort(sort), limit); - /// /// Get people's roles from a person. /// /// The id of the person /// A filter function - /// Sort informations (sort order & sort by) + /// Sort information (sort order & sort by) /// How many items to return and where to start /// A list of items that match every filters Task> GetRolesFromPeople(int id, @@ -427,7 +432,7 @@ namespace Kyoo.Abstractions.Controllers /// /// The slug of the person /// A filter function - /// Sort informations (sort order & sort by) + /// Sort information (sort order & sort by) /// How many items to return and where to start /// A list of items that match every filters Task> GetRolesFromPeople(string slug, @@ -449,13 +454,13 @@ namespace Kyoo.Abstractions.Controllers Pagination limit = default ) => GetRolesFromPeople(slug, where, new Sort(sort), limit); - /// /// Setup relations between a show, a library and a collection /// /// The show's ID to setup relations with /// The library's ID to setup relations with (optional) /// The collection's ID to setup relations with (optional) + /// A representing the asynchronous operation. Task AddShowLink(int showID, int? libraryID, int? collectionID); /// @@ -464,19 +469,21 @@ namespace Kyoo.Abstractions.Controllers /// The show to setup relations with /// The library to setup relations with (optional) /// The collection to setup relations with (optional) + /// A representing the asynchronous operation. Task AddShowLink([NotNull] Show show, Library library, Collection collection); /// /// Get all resources with filters /// /// A filter function - /// Sort informations (sort order & sort by) + /// Sort information (sort order & sort by) /// How many items to return and where to start /// The type of resources to load /// A list of resources that match every filters Task> GetAll(Expression> where = null, Sort sort = default, - Pagination limit = default) where T : class, IResource; + Pagination limit = default) + where T : class, IResource; /// /// Get all resources with filters @@ -488,7 +495,8 @@ namespace Kyoo.Abstractions.Controllers /// A list of resources that match every filters Task> GetAll([Optional] Expression> where, Expression> sort, - Pagination limit = default) where T : class, IResource + Pagination limit = default) + where T : class, IResource { return GetAll(where, new Sort(sort), limit); } @@ -499,7 +507,8 @@ namespace Kyoo.Abstractions.Controllers /// A filter function /// The type of resources to load /// A list of resources that match every filters - Task GetCount(Expression> where = null) where T : class, IResource; + Task GetCount(Expression> where = null) + where T : class, IResource; /// /// Search for a resource @@ -507,15 +516,17 @@ namespace Kyoo.Abstractions.Controllers /// The search query /// The type of resources /// A list of 20 items that match the search query - Task> Search(string query) where T : class, IResource; + Task> Search(string query) + where T : class, IResource; /// /// Create a new resource. /// /// The item to register /// The type of resource - /// The resource registers and completed by database's informations (related items & so on) - Task Create([NotNull] T item) where T : class, IResource; + /// The resource registers and completed by database's information (related items & so on) + Task Create([NotNull] T item) + where T : class, IResource; /// /// Create a new resource if it does not exist already. If it does, the existing value is returned instead. @@ -523,17 +534,19 @@ namespace Kyoo.Abstractions.Controllers /// The item to register /// The type of resource /// The newly created item or the existing value if it existed. - Task CreateIfNotExists([NotNull] T item) where T : class, IResource; + Task CreateIfNotExists([NotNull] T item) + where T : class, IResource; /// /// Edit a resource /// - /// The resourcce to edit, it's ID can't change. + /// The resource to edit, it's ID can't change. /// Should old properties of the resource be discarded or should null values considered as not changed? /// The type of resources /// If the item is not found - /// The resource edited and completed by database's informations (related items & so on) - Task Edit(T item, bool resetOld) where T : class, IResource; + /// The resource edited and completed by database's information (related items & so on) + Task Edit(T item, bool resetOld) + where T : class, IResource; /// /// Delete a resource. @@ -541,7 +554,9 @@ namespace Kyoo.Abstractions.Controllers /// The resource to delete /// The type of resource to delete /// If the item is not found - Task Delete(T item) where T : class, IResource; + /// A representing the asynchronous operation. + Task Delete(T item) + where T : class, IResource; /// /// Delete a resource by it's ID. @@ -549,7 +564,9 @@ namespace Kyoo.Abstractions.Controllers /// The id of the resource to delete /// The type of resource to delete /// If the item is not found - Task Delete(int id) where T : class, IResource; + /// A representing the asynchronous operation. + Task Delete(int id) + where T : class, IResource; /// /// Delete a resource by it's slug. @@ -557,6 +574,8 @@ namespace Kyoo.Abstractions.Controllers /// The slug of the resource to delete /// The type of resource to delete /// If the item is not found - Task Delete(string slug) where T : class, IResource; + /// A representing the asynchronous operation. + Task Delete(string slug) + where T : class, IResource; } } diff --git a/Kyoo.Abstractions/Controllers/IMetadataProvider.cs b/Kyoo.Abstractions/Controllers/IMetadataProvider.cs index c04ac906..73c0a974 100644 --- a/Kyoo.Abstractions/Controllers/IMetadataProvider.cs +++ b/Kyoo.Abstractions/Controllers/IMetadataProvider.cs @@ -1,7 +1,7 @@ -using Kyoo.Abstractions.Models; -using System.Collections.Generic; +using System.Collections.Generic; using System.Threading.Tasks; using JetBrains.Annotations; +using Kyoo.Abstractions.Models; namespace Kyoo.Abstractions.Controllers { @@ -30,6 +30,7 @@ namespace Kyoo.Abstractions.Controllers /// Merging metadata is the job of Kyoo, a complex is given /// to make a precise search and give you every available properties, not to discard properties. /// + /// The type of resource to retrieve metadata for. /// A new containing metadata from your provider or null [ItemCanBeNull] Task Get([NotNull] T item) @@ -39,6 +40,7 @@ namespace Kyoo.Abstractions.Controllers /// Search for a specific type of items with a given query. /// /// The search query to use. + /// The type of resource to search metadata for. /// The list of items that could be found on this specific provider. [ItemNotNull] Task> Search(string query) @@ -62,7 +64,7 @@ namespace Kyoo.Abstractions.Controllers /// /// Since this is a composite and not a real provider, no metadata is available. - /// It is not meant to be stored or selected. This class will handle merge based on what is required. + /// It is not meant to be stored or selected. This class will handle merge based on what is required. /// public Provider Provider => null; diff --git a/Kyoo.Abstractions/Controllers/IPermissionValidator.cs b/Kyoo.Abstractions/Controllers/IPermissionValidator.cs new file mode 100644 index 00000000..53bd8b83 --- /dev/null +++ b/Kyoo.Abstractions/Controllers/IPermissionValidator.cs @@ -0,0 +1,29 @@ +using Kyoo.Abstractions.Models.Permissions; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Kyoo.Abstractions.Controllers +{ + /// + /// A service to validate permissions. + /// + public interface IPermissionValidator + { + /// + /// Create an IAuthorizationFilter that will be used to validate permissions. + /// This can registered with any lifetime. + /// + /// The permission attribute to validate. + /// An authorization filter used to validate the permission. + IFilterMetadata Create(PermissionAttribute attribute); + + /// + /// Create an IAuthorizationFilter that will be used to validate permissions. + /// This can registered with any lifetime. + /// + /// + /// A partial attribute to validate. See . + /// + /// An authorization filter used to validate the permission. + IFilterMetadata Create(PartialPermissionAttribute attribute); + } +} diff --git a/Kyoo.Abstractions/Controllers/IPlugin.cs b/Kyoo.Abstractions/Controllers/IPlugin.cs index 507fbb39..6ea00db3 100644 --- a/Kyoo.Abstractions/Controllers/IPlugin.cs +++ b/Kyoo.Abstractions/Controllers/IPlugin.cs @@ -35,7 +35,7 @@ namespace Kyoo.Abstractions.Controllers /// true if the plugin should be enabled, false otherwise. /// If a plugin is not enabled, no configure method will be called. /// This allow one to enable a plugin if a specific configuration value is set or if the environment contains - /// the right settings. + /// the right settings. /// /// /// By default, a plugin is always enabled. This method can be overriden to change this behavior. diff --git a/Kyoo.Abstractions/Controllers/IRepository.cs b/Kyoo.Abstractions/Controllers/IRepository.cs index 5cc6c9a3..2898e9a9 100644 --- a/Kyoo.Abstractions/Controllers/IRepository.cs +++ b/Kyoo.Abstractions/Controllers/IRepository.cs @@ -19,8 +19,9 @@ namespace Kyoo.Abstractions.Controllers /// The count of items to return. /// public int Count { get; } + /// - /// Where to start? Using the given sort + /// Where to start? Using the given sort. /// public int AfterID { get; } @@ -53,6 +54,7 @@ namespace Kyoo.Abstractions.Controllers /// The sort key. This member will be used to sort the results. /// public Expression> Key { get; } + /// /// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order. /// @@ -121,7 +123,8 @@ namespace Kyoo.Abstractions.Controllers /// A common repository for every resources. /// /// The resource's type that this repository manage. - public interface IRepository : IBaseRepository where T : class, IResource + public interface IRepository : IBaseRepository + where T : class, IResource { /// /// Get a resource from it's ID. diff --git a/Kyoo.Abstractions/Models/Attributes/Permission/PartialPermissionAttribute.cs b/Kyoo.Abstractions/Models/Attributes/Permission/PartialPermissionAttribute.cs new file mode 100644 index 00000000..5cab9ad8 --- /dev/null +++ b/Kyoo.Abstractions/Models/Attributes/Permission/PartialPermissionAttribute.cs @@ -0,0 +1,70 @@ +using System; +using Kyoo.Abstractions.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Abstractions.Models.Permissions +{ + /// + /// Specify one part of a permissions needed for the API (the kind or the type). + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + public class PartialPermissionAttribute : Attribute, IFilterFactory + { + /// + /// The needed permission type. + /// + public string Type { get; } + + /// + /// The needed permission kind. + /// + public Kind Kind { get; } + + /// + /// Ask a permission to run an action. + /// + /// + /// With this attribute, you can only specify a type or a kind. + /// To have a valid permission attribute, you must specify the kind and the permission using two attributes. + /// Those attributes can be dispatched at different places (one on the class, one on the method for example). + /// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will + /// lead to unspecified behaviors. + /// + /// + /// The type of the action + /// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)). + /// + public PartialPermissionAttribute(string type) + { + if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase)) + type = type[..^3]; + Type = type.ToLower(); + } + + /// + /// Ask a permission to run an action. + /// + /// + /// With this attribute, you can only specify a type or a kind. + /// To have a valid permission attribute, you must specify the kind and the permission using two attributes. + /// Those attributes can be dispatched at different places (one on the class, one on the method for example). + /// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will + /// lead to unspecified behaviors. + /// + /// The kind of permission needed. + public PartialPermissionAttribute(Kind permission) + { + Kind = permission; + } + + /// + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) + { + return serviceProvider.GetRequiredService().Create(this); + } + + /// + public bool IsReusable => true; + } +} diff --git a/Kyoo.Abstractions/Models/Attributes/Permission/PermissionAttribute.cs b/Kyoo.Abstractions/Models/Attributes/Permission/PermissionAttribute.cs new file mode 100644 index 00000000..382170d6 --- /dev/null +++ b/Kyoo.Abstractions/Models/Attributes/Permission/PermissionAttribute.cs @@ -0,0 +1,110 @@ +using System; +using Kyoo.Abstractions.Controllers; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; + +namespace Kyoo.Abstractions.Models.Permissions +{ + /// + /// The kind of permission needed. + /// + public enum Kind + { + /// + /// Allow the user to read for this kind of data. + /// + Read, + + /// + /// Allow the user to write for this kind of data. + /// + Write, + + /// + /// Allow the user to create this kind of data. + /// + Create, + + /// + /// Allow the user to delete this kind od data. + /// + Delete + } + + /// + /// The group of the permission. + /// + public enum Group + { + /// + /// Allow all operations on basic items types. + /// + Overall, + + /// + /// Allow operation on sensitive items like libraries path, configurations and so on. + /// + Admin + } + + /// + /// Specify permissions needed for the API. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] + public class PermissionAttribute : Attribute, IFilterFactory + { + /// + /// The needed permission as string. + /// + public string Type { get; } + + /// + /// The needed permission kind. + /// + public Kind Kind { get; } + + /// + /// The group of this permission. + /// + public Group Group { get; } + + /// + /// Ask a permission to run an action. + /// + /// + /// The type of the action + /// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)). + /// + /// The kind of permission needed. + /// + /// The group of this permission (allow grouped permission like overall.read + /// for all read permissions of this group). + /// + public PermissionAttribute(string type, Kind permission, Group group = Group.Overall) + { + if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase)) + type = type[..^3]; + Type = type.ToLower(); + Kind = permission; + Group = group; + } + + /// + public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) + { + return serviceProvider.GetRequiredService().Create(this); + } + + /// + public bool IsReusable => true; + + /// + /// Return this permission attribute as a string. + /// + /// The string representation. + public string AsPermissionString() + { + return Type; + } + } +} diff --git a/Kyoo.Abstractions/Models/Attributes/PermissionAttribute.cs b/Kyoo.Abstractions/Models/Attributes/PermissionAttribute.cs deleted file mode 100644 index 19bd1961..00000000 --- a/Kyoo.Abstractions/Models/Attributes/PermissionAttribute.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System; -using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.Extensions.DependencyInjection; - -namespace Kyoo.Abstractions.Models.Permissions -{ - /// - /// The kind of permission needed. - /// - public enum Kind - { - Read, - Write, - Create, - Delete - } - - /// - /// The group of the permission. - /// - public enum Group - { - Overall, - Admin - } - - /// - /// Specify permissions needed for the API. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] - public class PermissionAttribute : Attribute, IFilterFactory - { - /// - /// The needed permission as string. - /// - public string Type { get; } - /// - /// The needed permission kind. - /// - public Kind Kind { get; } - /// - /// The group of this permission - /// - public Group Group { get; } - - /// - /// Ask a permission to run an action. - /// - /// - /// The type of the action - /// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)). - /// - /// The kind of permission needed - /// - /// The group of this permission (allow grouped permission like overall.read - /// for all read permissions of this group) - /// - public PermissionAttribute(string type, Kind permission, Group group = Group.Overall) - { - if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase)) - type = type[..^3]; - Type = type.ToLower(); - Kind = permission; - Group = group; - } - - /// - public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) - { - return serviceProvider.GetRequiredService().Create(this); - } - - /// - public bool IsReusable => true; - - /// - /// Return this permission attribute as a string - /// - /// The string representation. - public string AsPermissionString() - { - return Type; - } - } - - /// - /// Specify one part of a permissions needed for the API (the kind or the type). - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)] - public class PartialPermissionAttribute : Attribute, IFilterFactory - { - /// - /// The needed permission type. - /// - public string Type { get; } - /// - /// The needed permission kind. - /// - public Kind Kind { get; } - - /// - /// Ask a permission to run an action. - /// - /// - /// With this attribute, you can only specify a type or a kind. - /// To have a valid permission attribute, you must specify the kind and the permission using two attributes. - /// Those attributes can be dispatched at different places (one on the class, one on the method for example). - /// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will - /// lead to unspecified behaviors. - /// - /// - /// The type of the action - /// (if the type ends with api, it will be removed. This allow you to use nameof(YourApi)). - /// - public PartialPermissionAttribute(string type) - { - if (type.EndsWith("API", StringComparison.OrdinalIgnoreCase)) - type = type[..^3]; - Type = type.ToLower(); - } - - /// - /// Ask a permission to run an action. - /// - /// - /// With this attribute, you can only specify a type or a kind. - /// To have a valid permission attribute, you must specify the kind and the permission using two attributes. - /// Those attributes can be dispatched at different places (one on the class, one on the method for example). - /// If you don't put exactly two of those attributes, the permission attribute will be ill-formed and will - /// lead to unspecified behaviors. - /// - /// The kind of permission needed - public PartialPermissionAttribute(Kind permission) - { - Kind = permission; - } - - /// - public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) - { - return serviceProvider.GetRequiredService().Create(this); - } - - /// - public bool IsReusable => true; - } - - - /// - /// A service to validate permissions - /// - public interface IPermissionValidator - { - /// - /// Create an IAuthorizationFilter that will be used to validate permissions. - /// This can registered with any lifetime. - /// - /// The permission attribute to validate - /// An authorization filter used to validate the permission - IFilterMetadata Create(PermissionAttribute attribute); - - /// - /// Create an IAuthorizationFilter that will be used to validate permissions. - /// This can registered with any lifetime. - /// - /// - /// A partial attribute to validate. See . - /// - /// An authorization filter used to validate the permission - IFilterMetadata Create(PartialPermissionAttribute attribute); - } -} \ No newline at end of file diff --git a/Kyoo.Abstractions/Models/Resources/Show.cs b/Kyoo.Abstractions/Models/Resources/Show.cs index 1a4b87c9..a46fa8fb 100644 --- a/Kyoo.Abstractions/Models/Resources/Show.cs +++ b/Kyoo.Abstractions/Models/Resources/Show.cs @@ -50,7 +50,7 @@ namespace Kyoo.Abstractions.Models public string TrailerUrl => Images?.GetValueOrDefault(Models.Images.Trailer); /// - /// The date this show started airing. It can be null if this is unknown. + /// The date this show started airing. It can be null if this is unknown. /// public DateTime? StartAir { get; set; } @@ -103,6 +103,7 @@ namespace Kyoo.Abstractions.Models /// The ID of the Studio that made this show. /// [SerializeIgnore] public int? StudioID { get; set; } + /// /// The Studio that made this show. /// This must be explicitly loaded via a call to . @@ -145,19 +146,48 @@ namespace Kyoo.Abstractions.Models public void OnMerge(object merged) { if (People != null) + { foreach (PeopleRole link in People) link.Show = this; + } + if (Seasons != null) + { foreach (Season season in Seasons) season.Show = this; + } + if (Episodes != null) + { foreach (Episode episode in Episodes) episode.Show = this; + } } } /// /// The enum containing show's status. /// - public enum Status { Unknown, Finished, Airing, Planned } + public enum Status + { + /// + /// The status of the show is not known. + /// + Unknown, + + /// + /// The show has finished airing. + /// + Finished, + + /// + /// The show is still actively airing. + /// + Airing, + + /// + /// This show has not aired yet but has been announced. + /// + Planned + } } diff --git a/Kyoo.Authentication/AuthenticationModule.cs b/Kyoo.Authentication/AuthenticationModule.cs index 054b0268..f05c10ac 100644 --- a/Kyoo.Authentication/AuthenticationModule.cs +++ b/Kyoo.Authentication/AuthenticationModule.cs @@ -85,7 +85,7 @@ namespace Kyoo.Authentication /// public void Configure(ContainerBuilder builder) { - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); DefaultCorsPolicyService cors = new(_logger) { diff --git a/Kyoo.Authentication/Controllers/PremissionValidator.cs b/Kyoo.Authentication/Controllers/PermissionValidator.cs similarity index 77% rename from Kyoo.Authentication/Controllers/PremissionValidator.cs rename to Kyoo.Authentication/Controllers/PermissionValidator.cs index 05ef1a16..e4bdf0b3 100644 --- a/Kyoo.Authentication/Controllers/PremissionValidator.cs +++ b/Kyoo.Authentication/Controllers/PermissionValidator.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models.Permissions; using Kyoo.Authentication.Models; using Microsoft.AspNetCore.Authentication; @@ -17,7 +18,7 @@ namespace Kyoo.Authentication /// A permission validator to validate permission with user Permission array /// or the default array from the configurations if the user is not logged. /// - public class PermissionValidatorFactory : IPermissionValidator + public class PermissionValidator : IPermissionValidator { /// /// The permissions options to retrieve default permissions. @@ -25,10 +26,10 @@ namespace Kyoo.Authentication private readonly IOptionsMonitor _options; /// - /// Create a new factory with the given options + /// Create a new factory with the given options. /// /// The option containing default values. - public PermissionValidatorFactory(IOptionsMonitor options) + public PermissionValidator(IOptionsMonitor options) { _options = options; } @@ -36,46 +37,49 @@ namespace Kyoo.Authentication /// public IFilterMetadata Create(PermissionAttribute attribute) { - return new PermissionValidator(attribute.Type, attribute.Kind, attribute.Group, _options); + return new PermissionValidatorFilter(attribute.Type, attribute.Kind, attribute.Group, _options); } /// public IFilterMetadata Create(PartialPermissionAttribute attribute) { - return new PermissionValidator((object)attribute.Type ?? attribute.Kind, _options); + return new PermissionValidatorFilter((object)attribute.Type ?? attribute.Kind, _options); } /// - /// The authorization filter used by + /// The authorization filter used by . /// - private class PermissionValidator : IAsyncAuthorizationFilter + private class PermissionValidatorFilter : IAsyncAuthorizationFilter { /// - /// The permission to validate + /// The permission to validate. /// private readonly string _permission; + /// - /// The kind of permission needed + /// The kind of permission needed. /// private readonly Kind? _kind; /// - /// The group of he permission + /// The group of he permission. /// private readonly Group _group = Group.Overall; + /// /// The permissions options to retrieve default permissions. /// private readonly IOptionsMonitor _options; /// - /// Create a new permission validator with the given options + /// Create a new permission validator with the given options. /// - /// The permission to validate - /// The kind of permission needed - /// The group of the permission + /// The permission to validate. + /// The kind of permission needed. + /// The group of the permission. /// The option containing default values. - public PermissionValidator(string permission, Kind kind, Group group, IOptionsMonitor options) + public PermissionValidatorFilter(string permission, Kind kind, Group group, + IOptionsMonitor options) { _permission = permission; _kind = kind; @@ -84,11 +88,11 @@ namespace Kyoo.Authentication } /// - /// Create a new permission validator with the given options + /// Create a new permission validator with the given options. /// - /// The partial permission to validate + /// The partial permission to validate. /// The option containing default values. - public PermissionValidator(object partialInfo, IOptionsMonitor options) + public PermissionValidatorFilter(object partialInfo, IOptionsMonitor options) { if (partialInfo is Kind kind) _kind = kind; @@ -99,7 +103,6 @@ namespace Kyoo.Authentication _options = options; } - /// public async Task OnAuthorizationAsync(AuthorizationFilterContext context) { @@ -127,8 +130,10 @@ namespace Kyoo.Authentication "are not supported."); } if (permission == null || kind == null) + { throw new ArgumentException("The permission type or kind is still missing after two partial " + "permission attributes, this is unsupported."); + } } string permStr = $"{permission.ToLower()}.{kind.ToString()!.ToLower()}"; diff --git a/Kyoo.Core/Controllers/PassthroughPermissionValidator.cs b/Kyoo.Core/Controllers/PassthroughPermissionValidator.cs index ff9b07aa..a17d587a 100644 --- a/Kyoo.Core/Controllers/PassthroughPermissionValidator.cs +++ b/Kyoo.Core/Controllers/PassthroughPermissionValidator.cs @@ -1,3 +1,5 @@ +using System.Diagnostics.CodeAnalysis; +using Kyoo.Abstractions.Controllers; using Kyoo.Abstractions.Models.Permissions; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; @@ -9,7 +11,8 @@ namespace Kyoo.Core.Controllers /// public class PassthroughPermissionValidator : IPermissionValidator { - // ReSharper disable once SuggestBaseTypeForParameter + [SuppressMessage("ReSharper", "SuggestBaseTypeForParameterInConstructor", + Justification = "ILogger should include the typeparam for context.")] public PassthroughPermissionValidator(ILogger logger) { logger.LogWarning("No permission validator has been enabled, all users will have all permissions"); @@ -28,8 +31,8 @@ namespace Kyoo.Core.Controllers } /// - /// An useless filter that does nothing. + /// An useless filter that does nothing. /// private class PassthroughValidator : IFilterMetadata { } } -} \ No newline at end of file +} diff --git a/Kyoo.Core/Views/VideoApi.cs b/Kyoo.Core/Views/VideoApi.cs index 8a21a560..fbaf605e 100644 --- a/Kyoo.Core/Views/VideoApi.cs +++ b/Kyoo.Core/Views/VideoApi.cs @@ -41,9 +41,9 @@ namespace Kyoo.Core.Api } + // TODO enable the following line, this is disabled since the web app can't use bearers. [Permission("video", Kind.Read)] [HttpGet("{slug}")] [HttpGet("direct/{slug}")] - // TODO enable the following line, this is disabled since the web app can't use bearers. [Permission("video", Kind.Read)] public async Task Direct(string slug) { try @@ -114,4 +114,4 @@ namespace Kyoo.Core.Api return PhysicalFile(path, "video/MP2T"); } } -} \ No newline at end of file +} diff --git a/Kyoo.Database/DatabaseContext.cs b/Kyoo.Database/DatabaseContext.cs index 50d5ebeb..8819d1e6 100644 --- a/Kyoo.Database/DatabaseContext.cs +++ b/Kyoo.Database/DatabaseContext.cs @@ -26,38 +26,47 @@ namespace Kyoo.Database /// All libraries of Kyoo. See . /// public DbSet Libraries { get; set; } + /// /// All collections of Kyoo. See . /// public DbSet Collections { get; set; } + /// /// All shows of Kyoo. See . /// public DbSet Shows { get; set; } + /// /// All seasons of Kyoo. See . /// public DbSet Seasons { get; set; } + /// /// All episodes of Kyoo. See . /// public DbSet Episodes { get; set; } + /// /// All tracks of Kyoo. See . /// public DbSet Tracks { get; set; } + /// /// All genres of Kyoo. See . /// public DbSet Genres { get; set; } + /// /// All people of Kyoo. See . /// public DbSet People { get; set; } + /// /// All studios of Kyoo. See . /// public DbSet Studios { get; set; } + /// /// All providers of Kyoo. See . /// @@ -74,12 +83,12 @@ namespace Kyoo.Database public DbSet PeopleRoles { get; set; } /// - /// Episodes with a watch percentage. See + /// Episodes with a watch percentage. See . /// public DbSet WatchedEpisodes { get; set; } /// - /// The list of library items (shows and collections that are part of a library - or the global one) + /// The list of library items (shows and collections that are part of a library - or the global one). /// /// /// This set is ready only, on most database this will be a view. @@ -471,7 +480,7 @@ namespace Kyoo.Database /// A duplicated item has been found. /// The number of state entries written to the database. public async Task SaveChangesAsync(string duplicateMessage, - CancellationToken cancellationToken = new()) + CancellationToken cancellationToken = default) { try { @@ -492,7 +501,7 @@ namespace Kyoo.Database /// /// A to observe while waiting for the task to complete /// The number of state entries written to the database or -1 if a duplicate exist. - public async Task SaveIfNoDuplicates(CancellationToken cancellationToken = new()) + public async Task SaveIfNoDuplicates(CancellationToken cancellationToken = default) { try { @@ -507,7 +516,7 @@ namespace Kyoo.Database /// /// Return the first resource with the given slug that is currently tracked by this context. /// This allow one to limit redundant calls to during the - /// same transaction and prevent fails from EF when two same entities are being tracked. + /// same transaction and prevent fails from EF when two same entities are being tracked. /// /// The slug of the resource to check /// The type of entity to check @@ -548,4 +557,4 @@ namespace Kyoo.Database /// An expression representing the like query. It can directly be passed to a where call. public abstract Expression> Like(Expression> query, string format); } -} \ No newline at end of file +} diff --git a/Kyoo.TheMovieDb/ProviderTmdb.cs b/Kyoo.TheMovieDb/TheMovieDbProvider.cs similarity index 91% rename from Kyoo.TheMovieDb/ProviderTmdb.cs rename to Kyoo.TheMovieDb/TheMovieDbProvider.cs index 826e348b..87f9f2f5 100644 --- a/Kyoo.TheMovieDb/ProviderTmdb.cs +++ b/Kyoo.TheMovieDb/TheMovieDbProvider.cs @@ -23,6 +23,7 @@ namespace Kyoo.TheMovieDb /// The API key used to authenticate with TheMovieDb API. /// private readonly IOptions _apiKey; + /// /// The logger to use in ase of issue. /// @@ -43,7 +44,7 @@ namespace Kyoo.TheMovieDb /// /// Create a new using the given api key. /// - /// The api key + /// The api key. /// The logger to use in case of issue. public TheMovieDbProvider(IOptions apiKey, ILogger logger) { @@ -51,7 +52,6 @@ namespace Kyoo.TheMovieDb _logger = logger; } - /// public Task Get(T item) where T : class, IResource @@ -71,8 +71,8 @@ namespace Kyoo.TheMovieDb /// /// Get a collection using it's id, if the id is not present in the collection, fallback to a name search. /// - /// The collection to search for - /// A collection containing metadata from TheMovieDb + /// The collection to search for. + /// A collection containing metadata from TheMovieDb. private async Task _GetCollection(Collection collection) { if (!collection.TryGetID(Provider.Slug, out int id)) @@ -89,8 +89,8 @@ namespace Kyoo.TheMovieDb /// /// Get a show using it's id, if the id is not present in the show, fallback to a title search. /// - /// The show to search for - /// A show containing metadata from TheMovieDb + /// The show to search for. + /// A show containing metadata from TheMovieDb. private async Task _GetShow(Show show) { if (!show.TryGetID(Provider.Slug, out int id)) @@ -119,7 +119,7 @@ namespace Kyoo.TheMovieDb /// Get a season using it's show and it's season number. /// /// The season to retrieve metadata for. - /// A season containing metadata from TheMovieDb + /// A season containing metadata from TheMovieDb. private async Task _GetSeason(Season season) { if (season.Show == null) @@ -139,10 +139,10 @@ namespace Kyoo.TheMovieDb /// /// Get an episode using it's show, it's season number and it's episode number. - /// Absolute numbering is not supported. + /// Absolute numbering is not supported. /// /// The episode to retrieve metadata for. - /// An episode containing metadata from TheMovieDb + /// An episode containing metadata from TheMovieDb. private async Task _GetEpisode(Episode episode) { if (episode.Show == null) @@ -163,8 +163,8 @@ namespace Kyoo.TheMovieDb /// /// Get a person using it's id, if the id is not present in the person, fallback to a name search. /// - /// The person to search for - /// A person containing metadata from TheMovieDb + /// The person to search for. + /// A person containing metadata from TheMovieDb. private async Task _GetPerson(People person) { if (!person.TryGetID(Provider.Slug, out int id)) @@ -181,8 +181,8 @@ namespace Kyoo.TheMovieDb /// /// Get a studio using it's id, if the id is not present in the studio, fallback to a name search. /// - /// The studio to search for - /// A studio containing metadata from TheMovieDb + /// The studio to search for. + /// A studio containing metadata from TheMovieDb. private async Task _GetStudio(Studio studio) { if (!studio.TryGetID(Provider.Slug, out int id)) @@ -277,4 +277,4 @@ namespace Kyoo.TheMovieDb .ToArray(); } } -} \ No newline at end of file +} diff --git a/Kyoo.ruleset b/Kyoo.ruleset index 0ef4692c..a4415374 100644 --- a/Kyoo.ruleset +++ b/Kyoo.ruleset @@ -3,6 +3,10 @@ + + + + @@ -11,13 +15,20 @@ + + + + + + + @@ -27,5 +38,6 @@ +