mirror of
				https://github.com/zoriya/Kyoo.git
				synced 2025-11-03 19:17:16 -05:00 
			
		
		
		
	Reworking login requests
This commit is contained in:
		
							parent
							
								
									0f2bea9bc4
								
							
						
					
					
						commit
						d9cca97961
					
				
							
								
								
									
										54
									
								
								Kyoo.Authentication/Controllers/PasswordUtils.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								Kyoo.Authentication/Controllers/PasswordUtils.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,54 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Security.Cryptography;
 | 
				
			||||||
 | 
					using IdentityModel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Kyoo.Authentication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						public static class PasswordUtils
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Generate an OneTimeAccessCode.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <returns>A new otac.</returns>
 | 
				
			||||||
 | 
							public static string GenerateOTAC()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return CryptoRandom.CreateUniqueId();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Hash a password to store it has a verification only.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="password">The password to hash</param>
 | 
				
			||||||
 | 
							/// <returns>The hashed password</returns>
 | 
				
			||||||
 | 
							public static string HashPassword(string password)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								byte[] salt = new byte[16];
 | 
				
			||||||
 | 
								new RNGCryptoServiceProvider().GetBytes(salt);
 | 
				
			||||||
 | 
								Rfc2898DeriveBytes pbkdf2 = new(password, salt, 100000);
 | 
				
			||||||
 | 
								byte[] hash = pbkdf2.GetBytes(20);
 | 
				
			||||||
 | 
								byte[] hashBytes = new byte[36];
 | 
				
			||||||
 | 
								Array.Copy(salt, 0, hashBytes, 0, 16);
 | 
				
			||||||
 | 
								Array.Copy(hash, 0, hashBytes, 16, 20);
 | 
				
			||||||
 | 
								return Convert.ToBase64String(hashBytes);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Check if a password is the same as a valid hashed password.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="password">The password to check</param>
 | 
				
			||||||
 | 
							/// <param name="validPassword">
 | 
				
			||||||
 | 
							/// The valid hashed password. This password must be hashed via <see cref="HashPassword"/>.
 | 
				
			||||||
 | 
							/// </param>
 | 
				
			||||||
 | 
							/// <returns>True if the password is valid, false otherwise.</returns>
 | 
				
			||||||
 | 
							public static bool CheckPassword(string password, string validPassword)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								byte[] validHash = Convert.FromBase64String(validPassword);
 | 
				
			||||||
 | 
								byte[] salt = new byte[16];
 | 
				
			||||||
 | 
								Array.Copy(validHash, 0, salt, 0, 16);
 | 
				
			||||||
 | 
								Rfc2898DeriveBytes pbkdf2 = new(password, salt, 100000);
 | 
				
			||||||
 | 
								byte[] hash = pbkdf2.GetBytes(20);
 | 
				
			||||||
 | 
								return hash.SequenceEqual(validHash.Skip(16));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										26
									
								
								Kyoo.Authentication/Extensions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								Kyoo.Authentication/Extensions.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Security.Claims;
 | 
				
			||||||
 | 
					using IdentityModel;
 | 
				
			||||||
 | 
					using Kyoo.Models;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Kyoo.Authentication
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// Extension methods.
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public static class Extensions
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							public static ClaimsPrincipal ToPrincipal(this User user)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								List<Claim> claims = new()
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									new Claim(JwtClaimTypes.Subject, user.ID.ToString()),
 | 
				
			||||||
 | 
									new Claim(JwtClaimTypes.Name, user.Username),
 | 
				
			||||||
 | 
									new Claim(JwtClaimTypes.Picture, $"api/account/picture/{user.Slug}")
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ClaimsIdentity id = new (claims);
 | 
				
			||||||
 | 
								return new ClaimsPrincipal(id);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -19,6 +19,7 @@
 | 
				
			|||||||
		<PackageReference Include="IdentityServer4" Version="4.1.2" />
 | 
							<PackageReference Include="IdentityServer4" Version="4.1.2" />
 | 
				
			||||||
		<PackageReference Include="IdentityServer4.Storage" Version="4.1.2" />
 | 
							<PackageReference Include="IdentityServer4.Storage" Version="4.1.2" />
 | 
				
			||||||
		<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
 | 
							<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
 | 
				
			||||||
 | 
							<PackageReference Include="Microsoft.AspNetCore.Authentication" Version="2.2.0" />
 | 
				
			||||||
		<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
 | 
							<PackageReference Include="Portable.BouncyCastle" Version="1.8.10" />
 | 
				
			||||||
	</ItemGroup>
 | 
						</ItemGroup>
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										28
									
								
								Kyoo.Authentication/Models/DTO/LoginRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Kyoo.Authentication/Models/DTO/LoginRequest.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					namespace Kyoo.Authentication.Models.DTO
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// A model only used on login requests.
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public class LoginRequest
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The user's username.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public string Username { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The user's password.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public string Password { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Should the user stay logged in? If true a cookie will be put.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public bool StayLoggedIn { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The return url of the login flow.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public string ReturnURL { get; set; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										18
									
								
								Kyoo.Authentication/Models/DTO/OtacRequest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								Kyoo.Authentication/Models/DTO/OtacRequest.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,18 @@
 | 
				
			|||||||
 | 
					namespace Kyoo.Authentication.Models.DTO
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// A model to represent an otac request
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public class OtacRequest
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The One Time Access Code
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public string Otac { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Should the user stay logged
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public bool StayLoggedIn { get; set; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										28
									
								
								Kyoo.Authentication/Models/Options/AuthenticationOptions.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								Kyoo.Authentication/Models/Options/AuthenticationOptions.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					namespace Kyoo.Authentication.Models
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// The main authentication options.
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public class AuthenticationOptions
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The path to get this option from the root configuration.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public const string Path = "authentication";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The options for certificates
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public CertificateOption Certificate { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Options for permissions
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public PermissionOption Permissions { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Root path of user's profile pictures. 
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public string ProfilePicturePath { get; set; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -15,11 +15,11 @@ namespace Kyoo.Authentication.Models
 | 
				
			|||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// The default permissions that will be given to a non-connected user.
 | 
							/// The default permissions that will be given to a non-connected user.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public ICollection<string> Default { get; set; }
 | 
							public string[] Default { get; set; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Permissions applied to a new user.
 | 
							/// Permissions applied to a new user.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public ICollection<string> NewUser { get; set; }
 | 
							public string[] NewUser { get; set; }
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -1,183 +1,218 @@
 | 
				
			|||||||
// using System;
 | 
					using System;
 | 
				
			||||||
// using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
// using System.IO;
 | 
					using System.Globalization;
 | 
				
			||||||
// using System.Linq;
 | 
					using System.IO;
 | 
				
			||||||
// using System.Security.Claims;
 | 
					using System.Security.Claims;
 | 
				
			||||||
// using System.Threading.Tasks;
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
// using IdentityServer4.Models;
 | 
					using IdentityModel;
 | 
				
			||||||
// using IdentityServer4.Services;
 | 
					using IdentityServer4.Models;
 | 
				
			||||||
// using Kyoo.Authentication.Models.DTO;
 | 
					using IdentityServer4.Services;
 | 
				
			||||||
// using Kyoo.Models;
 | 
					using Kyoo.Authentication.Models.DTO;
 | 
				
			||||||
// using Microsoft.AspNetCore.Authorization;
 | 
					using Kyoo.Controllers;
 | 
				
			||||||
// using Microsoft.AspNetCore.Http;
 | 
					using Kyoo.Models;
 | 
				
			||||||
// using Microsoft.AspNetCore.Identity;
 | 
					using Microsoft.AspNetCore.Authorization;
 | 
				
			||||||
// using Microsoft.AspNetCore.Mvc;
 | 
					using Microsoft.AspNetCore.Authentication;
 | 
				
			||||||
// using Microsoft.Extensions.Configuration;
 | 
					using Microsoft.AspNetCore.Http;
 | 
				
			||||||
// using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;
 | 
					using Microsoft.AspNetCore.Mvc;
 | 
				
			||||||
//
 | 
					using Microsoft.Extensions.Options;
 | 
				
			||||||
// namespace Kyoo.Authentication.Views
 | 
					using AuthenticationOptions = Kyoo.Authentication.Models.AuthenticationOptions;
 | 
				
			||||||
// {
 | 
					
 | 
				
			||||||
// 	public class LoginRequest
 | 
					namespace Kyoo.Authentication.Views
 | 
				
			||||||
// 	{
 | 
					{
 | 
				
			||||||
// 		public string Username { get; set; }
 | 
						public class AccountData
 | 
				
			||||||
// 		public string Password { get; set; }
 | 
						{
 | 
				
			||||||
// 		public bool StayLoggedIn { get; set; }
 | 
							[FromForm(Name = "email")]
 | 
				
			||||||
// 	}
 | 
							public string Email { get; set; }
 | 
				
			||||||
//
 | 
							[FromForm(Name = "username")]
 | 
				
			||||||
// 	public class OtacRequest
 | 
							public string Username { get; set; }
 | 
				
			||||||
// 	{
 | 
							[FromForm(Name = "picture")]
 | 
				
			||||||
// 		public string Otac { get; set; }
 | 
							public IFormFile Picture { get; set; }
 | 
				
			||||||
// 		public bool StayLoggedIn { get; set; }
 | 
						}
 | 
				
			||||||
// 	}
 | 
					
 | 
				
			||||||
// 	
 | 
					
 | 
				
			||||||
// 	public class AccountData
 | 
						/// <summary>
 | 
				
			||||||
// 	{
 | 
						/// The class responsible for login, logout, permissions and claims of a user.
 | 
				
			||||||
// 		[FromForm(Name = "email")]
 | 
						/// </summary>
 | 
				
			||||||
// 		public string Email { get; set; }
 | 
						[Route("api/account")]
 | 
				
			||||||
// 		[FromForm(Name = "username")]
 | 
						[Route("api/accounts")]
 | 
				
			||||||
// 		public string Username { get; set; }
 | 
						[ApiController]
 | 
				
			||||||
// 		[FromForm(Name = "picture")]
 | 
						public class AccountApi : Controller, IProfileService
 | 
				
			||||||
// 		public IFormFile Picture { get; set; }
 | 
						{
 | 
				
			||||||
// 	}
 | 
							/// <summary>
 | 
				
			||||||
//
 | 
							/// The repository to handle users.
 | 
				
			||||||
//
 | 
							/// </summary>
 | 
				
			||||||
// 	/// <summary>
 | 
							private readonly IUserRepository _users;
 | 
				
			||||||
// 	/// The class responsible for login, logout, permissions and claims of a user.
 | 
							/// <summary>
 | 
				
			||||||
// 	/// </summary>
 | 
							/// The identity server interaction service to login users.
 | 
				
			||||||
// 	[Route("api/account")]
 | 
							/// </summary>
 | 
				
			||||||
// 	[Route("api/accounts")]
 | 
							private readonly IIdentityServerInteractionService _interaction;
 | 
				
			||||||
// 	[ApiController]
 | 
							/// <summary>
 | 
				
			||||||
// 	public class AccountApi : Controller, IProfileService
 | 
							/// Options about authentication. Those options are monitored and reloads are supported.
 | 
				
			||||||
// 	{
 | 
							/// </summary>
 | 
				
			||||||
// 		private readonly UserManager<User> _userManager;
 | 
							private readonly IOptionsMonitor<AuthenticationOptions> _options;
 | 
				
			||||||
// 		private readonly SignInManager<User> _signInManager;
 | 
					
 | 
				
			||||||
// 		private readonly IConfiguration _configuration;
 | 
					
 | 
				
			||||||
// 		private readonly string _picturePath;
 | 
							/// <summary>
 | 
				
			||||||
//
 | 
							/// Create a new <see cref="AccountApi"/> handle to handle login/users requests.
 | 
				
			||||||
// 		// TODO find how SignInManager & UserManager are implement and check if they can be used or not.
 | 
							/// </summary>
 | 
				
			||||||
// 		public AccountApi(UserManager<User> userManager, 
 | 
							/// <param name="users">The user repository to create and manage users</param>
 | 
				
			||||||
// 			SignInManager<User> signInManager, 
 | 
							/// <param name="interaction">The identity server interaction service to login users.</param>
 | 
				
			||||||
// 			IConfiguration configuration)
 | 
							/// <param name="options">Authentication options (this may be hot reloaded)</param>
 | 
				
			||||||
// 		{
 | 
							public AccountApi(IUserRepository users,
 | 
				
			||||||
// 			_userManager = userManager;
 | 
								IIdentityServerInteractionService interaction,
 | 
				
			||||||
// 			_signInManager = signInManager;
 | 
								IOptionsMonitor<AuthenticationOptions> options)
 | 
				
			||||||
// 			_picturePath = configuration.GetValue<string>("profilePicturePath");
 | 
							{
 | 
				
			||||||
// 			_configuration = configuration;
 | 
								_users = users;
 | 
				
			||||||
// 			if (!Path.IsPathRooted(_picturePath))
 | 
								_interaction = interaction;
 | 
				
			||||||
// 				_picturePath = Path.GetFullPath(_picturePath);
 | 
								_options = options;
 | 
				
			||||||
// 		}
 | 
							}
 | 
				
			||||||
// 		
 | 
							
 | 
				
			||||||
// 		[HttpPost("register")]
 | 
							
 | 
				
			||||||
// 		public async Task<IActionResult> Register([FromBody] RegisterRequest request)
 | 
							/// <summary>
 | 
				
			||||||
// 		{
 | 
							/// Register a new user and return a OTAC to connect to it. 
 | 
				
			||||||
// 			User user = request.ToUser();
 | 
							/// </summary>
 | 
				
			||||||
// 			IdentityResult result = await _userManager.CreateAsync(user, user.Password);
 | 
							/// <param name="request">The DTO register request</param>
 | 
				
			||||||
// 			if (!result.Succeeded)
 | 
							/// <returns>A OTAC to connect to this new account</returns>
 | 
				
			||||||
// 				return BadRequest(result.Errors);
 | 
							[HttpPost("register")]
 | 
				
			||||||
// 			string otac = account.GenerateOTAC(TimeSpan.FromMinutes(1));
 | 
							public async Task<ActionResult<string>> Register([FromBody] RegisterRequest request)
 | 
				
			||||||
// 			await _userManager.UpdateAsync(account);
 | 
							{
 | 
				
			||||||
// 			await _userManager.AddClaimAsync(account, new Claim(
 | 
								User user = request.ToUser();
 | 
				
			||||||
// 				"permissions",
 | 
								user.Permissions = _options.CurrentValue.Permissions.NewUser;
 | 
				
			||||||
// 				_configuration.GetValue<string>("newUserPermissions")));
 | 
								user.Password = PasswordUtils.HashPassword(user.Password);
 | 
				
			||||||
// 			return Ok(new {otac});
 | 
								user.ExtraData["otac"] = PasswordUtils.GenerateOTAC();
 | 
				
			||||||
// 		}
 | 
								user.ExtraData["otac-expire"] = DateTime.Now.AddMinutes(1).ToString("s");
 | 
				
			||||||
// 		
 | 
								await _users.Create(user);
 | 
				
			||||||
// 		[HttpPost("login")]
 | 
								return user.ExtraData["otac"];
 | 
				
			||||||
// 		public async Task<IActionResult> Login([FromBody] LoginRequest login)
 | 
							}
 | 
				
			||||||
// 		{
 | 
					
 | 
				
			||||||
// 			if (!ModelState.IsValid)
 | 
							/// <summary>
 | 
				
			||||||
// 				return BadRequest(login);
 | 
							/// Return an authentication properties based on a stay login property
 | 
				
			||||||
// 			SignInResult result = await _signInManager
 | 
							/// </summary>
 | 
				
			||||||
// 				.PasswordSignInAsync(login.Username, login.Password, login.StayLoggedIn, false);
 | 
							/// <param name="stayLogged">Should the user stay logged</param>
 | 
				
			||||||
// 			if (result.Succeeded)
 | 
							/// <returns>Authentication properties based on a stay login</returns>
 | 
				
			||||||
// 				return Ok();
 | 
							private static AuthenticationProperties StayLogged(bool stayLogged)
 | 
				
			||||||
// 			return BadRequest(new [] { new {code = "InvalidCredentials", description = "Invalid username/password"}});
 | 
							{
 | 
				
			||||||
// 		}
 | 
								if (!stayLogged)
 | 
				
			||||||
// 		
 | 
									return null;
 | 
				
			||||||
// 		[HttpPost("otac-login")]
 | 
								return new AuthenticationProperties
 | 
				
			||||||
// 		public async Task<IActionResult> OtacLogin([FromBody] OtacRequest otac)
 | 
								{
 | 
				
			||||||
// 		{
 | 
									IsPersistent = true,
 | 
				
			||||||
// 			if (!ModelState.IsValid)
 | 
									ExpiresUtc = DateTimeOffset.UtcNow.AddMonths(1)
 | 
				
			||||||
// 				return BadRequest(otac);
 | 
								};
 | 
				
			||||||
// 			User user = _userManager.Users.FirstOrDefault(x => x.OTAC == otac.Otac);
 | 
							}
 | 
				
			||||||
// 			if (user == null)
 | 
							
 | 
				
			||||||
// 				return BadRequest(new [] { new {code = "InvalidOTAC", description = "No user was found for this OTAC."}});
 | 
							/// <summary>
 | 
				
			||||||
// 			if (user.OTACExpires <= DateTime.UtcNow)
 | 
							/// Login the user.
 | 
				
			||||||
// 				return BadRequest(new [] { new {code = "ExpiredOTAC", description = "The OTAC has expired. Try to login with your password."}});
 | 
							/// </summary>
 | 
				
			||||||
// 			await _signInManager.SignInAsync(user, otac.StayLoggedIn);
 | 
							/// <param name="login">The DTO login request</param>
 | 
				
			||||||
// 			return Ok();
 | 
							[HttpPost("login")]
 | 
				
			||||||
// 		}
 | 
							public async Task<IActionResult> Login([FromBody] LoginRequest login)
 | 
				
			||||||
// 		
 | 
							{
 | 
				
			||||||
// 		[HttpGet("logout")]
 | 
								AuthorizationRequest context = await _interaction.GetAuthorizationContextAsync(login.ReturnURL);
 | 
				
			||||||
// 		[Authorize]
 | 
								User user = await _users.Get(x => x.Username == login.Username);
 | 
				
			||||||
// 		public async Task<IActionResult> Logout()
 | 
					
 | 
				
			||||||
// 		{
 | 
								if (context == null || user == null)
 | 
				
			||||||
// 			await _signInManager.SignOutAsync();
 | 
									return Unauthorized();
 | 
				
			||||||
// 			return Ok();
 | 
								if (!PasswordUtils.CheckPassword(login.Password, user.Password))
 | 
				
			||||||
// 		}
 | 
									return Unauthorized();
 | 
				
			||||||
//
 | 
					
 | 
				
			||||||
// 		public async Task GetProfileDataAsync(ProfileDataRequestContext context)
 | 
								await HttpContext.SignInAsync(user.ID.ToString(), user.ToPrincipal(), StayLogged(login.StayLoggedIn));
 | 
				
			||||||
// 		{
 | 
								return Ok(new { RedirectUrl = login.ReturnURL, IsOk = true });
 | 
				
			||||||
// 			User user = await _userManager.GetUserAsync(context.Subject);
 | 
							}
 | 
				
			||||||
// 			if (user != null)
 | 
							
 | 
				
			||||||
// 			{
 | 
							/// <summary>
 | 
				
			||||||
// 				List<Claim> claims = new()
 | 
							/// Use a OTAC to login a user.
 | 
				
			||||||
// 				{
 | 
							/// </summary>
 | 
				
			||||||
// 					new Claim("email", user.Email),
 | 
							/// <param name="otac">The OTAC request</param>
 | 
				
			||||||
// 					new Claim("username", user.UserName),
 | 
							[HttpPost("otac-login")]
 | 
				
			||||||
// 					new Claim("picture", $"api/account/picture/{user.UserName}")
 | 
							public async Task<IActionResult> OtacLogin([FromBody] OtacRequest otac)
 | 
				
			||||||
// 				};
 | 
							{
 | 
				
			||||||
//
 | 
								User user = await _users.Get(x => x.ExtraData["OTAC"] == otac.Otac);
 | 
				
			||||||
// 				Claim perms = (await _userManager.GetClaimsAsync(user)).FirstOrDefault(x => x.Type == "permissions");
 | 
								if (user == null)
 | 
				
			||||||
// 				if (perms != null)
 | 
									return Unauthorized();
 | 
				
			||||||
// 					claims.Add(perms);
 | 
								if (DateTime.ParseExact(user.ExtraData["otac-expire"], "s", CultureInfo.InvariantCulture) <= DateTime.UtcNow)
 | 
				
			||||||
// 				
 | 
									return BadRequest(new
 | 
				
			||||||
// 				context.IssuedClaims.AddRange(claims);
 | 
									{
 | 
				
			||||||
// 			}
 | 
										code = "ExpiredOTAC", description = "The OTAC has expired. Try to login with your password."
 | 
				
			||||||
// 		}
 | 
									});
 | 
				
			||||||
//
 | 
								await HttpContext.SignInAsync(user.ID.ToString(), user.ToPrincipal(), StayLogged(otac.StayLoggedIn));
 | 
				
			||||||
// 		public async Task IsActiveAsync(IsActiveContext context)
 | 
								return Ok();
 | 
				
			||||||
// 		{
 | 
							}
 | 
				
			||||||
// 			User user = await _userManager.GetUserAsync(context.Subject);
 | 
							
 | 
				
			||||||
// 			context.IsActive = user != null;
 | 
							/// <summary>
 | 
				
			||||||
// 		}
 | 
							/// Sign out an user
 | 
				
			||||||
// 		
 | 
							/// </summary>
 | 
				
			||||||
// 		[HttpGet("picture/{username}")]
 | 
							[HttpGet("logout")]
 | 
				
			||||||
// 		public async Task<IActionResult> GetPicture(string username)
 | 
							[Authorize]
 | 
				
			||||||
// 		{
 | 
							public async Task<IActionResult> Logout()
 | 
				
			||||||
// 			User user = await _userManager.FindByNameAsync(username);
 | 
							{
 | 
				
			||||||
// 			if (user == null)
 | 
								await HttpContext.SignOutAsync();
 | 
				
			||||||
// 				return BadRequest();
 | 
								return Ok();
 | 
				
			||||||
// 			string path = Path.Combine(_picturePath, user.Id);
 | 
							}
 | 
				
			||||||
// 			if (!System.IO.File.Exists(path))
 | 
					
 | 
				
			||||||
// 				return NotFound();
 | 
							// TODO check with the extension method
 | 
				
			||||||
// 			return new PhysicalFileResult(path, "image/png");
 | 
							public async Task GetProfileDataAsync(ProfileDataRequestContext context)
 | 
				
			||||||
// 		}
 | 
							{
 | 
				
			||||||
// 		
 | 
								User user = await _userManager.GetUserAsync(context.Subject);
 | 
				
			||||||
// 		[HttpPost("update")]
 | 
								if (user != null)
 | 
				
			||||||
// 		[Authorize]
 | 
								{
 | 
				
			||||||
// 		public async Task<IActionResult> Update([FromForm] AccountData data)
 | 
									List<Claim> claims = new()
 | 
				
			||||||
// 		{
 | 
									{
 | 
				
			||||||
// 			User user = await _userManager.GetUserAsync(HttpContext.User);
 | 
										new Claim(JwtClaimTypes.Email, user.Email),
 | 
				
			||||||
// 			
 | 
										new Claim(JwtClaimTypes.Name, user.Username),
 | 
				
			||||||
// 			if (!string.IsNullOrEmpty(data.Email))
 | 
										new Claim(JwtClaimTypes.Picture, $"api/account/picture/{user.Slug}")
 | 
				
			||||||
// 				user.Email =  data.Email;
 | 
									};
 | 
				
			||||||
// 			if (!string.IsNullOrEmpty(data.Username))
 | 
					
 | 
				
			||||||
// 				user.UserName = data.Username;
 | 
									Claim perms = (await _userManager.GetClaimsAsync(user)).FirstOrDefault(x => x.Type == "permissions");
 | 
				
			||||||
// 			if (data.Picture?.Length > 0)
 | 
									if (perms != null)
 | 
				
			||||||
// 			{
 | 
										claims.Add(perms);
 | 
				
			||||||
// 				string path = Path.Combine(_picturePath, user.Id);
 | 
									
 | 
				
			||||||
// 				await using FileStream file = System.IO.File.Create(path);
 | 
									context.IssuedClaims.AddRange(claims);
 | 
				
			||||||
// 				await data.Picture.CopyToAsync(file);
 | 
								}
 | 
				
			||||||
// 			}
 | 
							}
 | 
				
			||||||
// 			await _userManager.UpdateAsync(user);
 | 
					
 | 
				
			||||||
// 			return Ok();
 | 
							public async Task IsActiveAsync(IsActiveContext context)
 | 
				
			||||||
// 		}
 | 
							{
 | 
				
			||||||
//
 | 
								User user = await _userManager.GetUserAsync(context.Subject);
 | 
				
			||||||
// 		[HttpGet("default-permissions")]
 | 
								context.IsActive = user != null;
 | 
				
			||||||
// 		public ActionResult<IEnumerable<string>> GetDefaultPermissions()
 | 
							}
 | 
				
			||||||
// 		{
 | 
							
 | 
				
			||||||
// 			return _configuration.GetValue<string>("defaultPermissions").Split(",");
 | 
							[HttpGet("picture/{username}")]
 | 
				
			||||||
// 		}
 | 
							public async Task<IActionResult> GetPicture(string username)
 | 
				
			||||||
// 	}
 | 
							{
 | 
				
			||||||
// }
 | 
								User user = await _userManager.FindByNameAsync(username);
 | 
				
			||||||
 | 
								if (user == null)
 | 
				
			||||||
 | 
									return BadRequest();
 | 
				
			||||||
 | 
								string path = Path.Combine(_picturePath, user.Id);
 | 
				
			||||||
 | 
								if (!System.IO.File.Exists(path))
 | 
				
			||||||
 | 
									return NotFound();
 | 
				
			||||||
 | 
								return new PhysicalFileResult(path, "image/png");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							[HttpPost("update")]
 | 
				
			||||||
 | 
							[Authorize]
 | 
				
			||||||
 | 
							public async Task<IActionResult> Update([FromForm] AccountData data)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								User user = await _userManager.GetUserAsync(HttpContext.User);
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								if (!string.IsNullOrEmpty(data.Email))
 | 
				
			||||||
 | 
									user.Email =  data.Email;
 | 
				
			||||||
 | 
								if (!string.IsNullOrEmpty(data.Username))
 | 
				
			||||||
 | 
									user.UserName = data.Username;
 | 
				
			||||||
 | 
								if (data.Picture?.Length > 0)
 | 
				
			||||||
 | 
								{
 | 
				
			||||||
 | 
									string path = Path.Combine(_picturePath, user.Id);
 | 
				
			||||||
 | 
									await using FileStream file = System.IO.File.Create(path);
 | 
				
			||||||
 | 
									await data.Picture.CopyToAsync(file);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								await _userManager.UpdateAsync(user);
 | 
				
			||||||
 | 
								return Ok();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							[HttpGet("default-permissions")]
 | 
				
			||||||
 | 
							public ActionResult<IEnumerable<string>> GetDefaultPermissions()
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return _configuration.GetValue<string>("defaultPermissions").Split(",");
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -11,7 +11,7 @@ using Kyoo.Models.Exceptions;
 | 
				
			|||||||
namespace Kyoo.Controllers
 | 
					namespace Kyoo.Controllers
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/// <summary>
 | 
						/// <summary>
 | 
				
			||||||
	/// Informations about the pagination. How many items should be displayed and where to start.
 | 
						/// Information about the pagination. How many items should be displayed and where to start.
 | 
				
			||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	public readonly struct Pagination
 | 
						public readonly struct Pagination
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
@ -44,7 +44,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// <summary>
 | 
						/// <summary>
 | 
				
			||||||
	/// Informations about how a query should be sorted. What factor should decide the sort and in which order.
 | 
						/// Information about how a query should be sorted. What factor should decide the sort and in which order.
 | 
				
			||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	/// <typeparam name="T">For witch type this sort applies</typeparam>
 | 
						/// <typeparam name="T">For witch type this sort applies</typeparam>
 | 
				
			||||||
	public readonly struct Sort<T>
 | 
						public readonly struct Sort<T>
 | 
				
			||||||
@ -54,7 +54,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public Expression<Func<T, object>> Key { get; }
 | 
							public Expression<Func<T, object>> Key { get; }
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendent order.
 | 
							/// If this is set to true, items will be sorted in descend order else, they will be sorted in ascendant order.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public bool Descendant { get; }
 | 
							public bool Descendant { get; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
@ -175,7 +175,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// Get every resources that match all filters
 | 
							/// Get every resources that match all filters
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="where">A filter predicate</param>
 | 
							/// <param name="where">A filter predicate</param>
 | 
				
			||||||
		/// <param name="sort">Sort informations about the query (sort by, sort order)</param>
 | 
							/// <param name="sort">Sort information about the query (sort by, sort order)</param>
 | 
				
			||||||
		/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
 | 
							/// <param name="limit">How pagination should be done (where to start and how many to return)</param>
 | 
				
			||||||
		/// <returns>A list of resources that match every filters</returns>
 | 
							/// <returns>A list of resources that match every filters</returns>
 | 
				
			||||||
		Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null, 
 | 
							Task<ICollection<T>> GetAll(Expression<Func<T, bool>> where = null, 
 | 
				
			||||||
@ -205,24 +205,24 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// Create a new resource.
 | 
							/// Create a new resource.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="obj">The item to register</param>
 | 
							/// <param name="obj">The item to register</param>
 | 
				
			||||||
		/// <returns>The resource registers and completed by database's informations (related items & so on)</returns>
 | 
							/// <returns>The resource registers and completed by database's information (related items & so on)</returns>
 | 
				
			||||||
		Task<T> Create([NotNull] T obj);
 | 
							Task<T> Create([NotNull] T obj);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
 | 
							/// Create a new resource if it does not exist already. If it does, the existing value is returned instead.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="obj">The object to create</param>
 | 
							/// <param name="obj">The object to create</param>
 | 
				
			||||||
		/// <param name="silentFail">Allow issues to occurs in this method. Every issue is catched and ignored.</param>
 | 
							/// <param name="silentFail">Allow issues to occurs in this method. Every issue is caught and ignored.</param>
 | 
				
			||||||
		/// <returns>The newly created item or the existing value if it existed.</returns>
 | 
							/// <returns>The newly created item or the existing value if it existed.</returns>
 | 
				
			||||||
		Task<T> CreateIfNotExists([NotNull] T obj, bool silentFail = false);
 | 
							Task<T> CreateIfNotExists([NotNull] T obj, bool silentFail = false);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Edit a resource
 | 
							/// Edit a resource
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="edited">The resourcce to edit, it's ID can't change.</param>
 | 
							/// <param name="edited">The resource to edit, it's ID can't change.</param>
 | 
				
			||||||
		/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
 | 
							/// <param name="resetOld">Should old properties of the resource be discarded or should null values considered as not changed?</param>
 | 
				
			||||||
		/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
							/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
				
			||||||
		/// <returns>The resource edited and completed by database's informations (related items & so on)</returns>
 | 
							/// <returns>The resource edited and completed by database's information (related items & so on)</returns>
 | 
				
			||||||
		Task<T> Edit([NotNull] T edited, bool resetOld);
 | 
							Task<T> Edit([NotNull] T edited, bool resetOld);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
@ -259,25 +259,25 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Delete a list of resources.
 | 
							/// Delete a list of resources.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="ids">One or multiple resources's id</param>
 | 
							/// <param name="ids">One or multiple resource's id</param>
 | 
				
			||||||
		/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
							/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
				
			||||||
		Task DeleteRange(params int[] ids) => DeleteRange(ids.AsEnumerable());
 | 
							Task DeleteRange(params int[] ids) => DeleteRange(ids.AsEnumerable());
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Delete a list of resources.
 | 
							/// Delete a list of resources.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="ids">An enumearble of resources's id</param>
 | 
							/// <param name="ids">An enumerable of resource's id</param>
 | 
				
			||||||
		/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
							/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
				
			||||||
		Task DeleteRange(IEnumerable<int> ids);
 | 
							Task DeleteRange(IEnumerable<int> ids);
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Delete a list of resources.
 | 
							/// Delete a list of resources.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="slugs">One or multiple resources's slug</param>
 | 
							/// <param name="slugs">One or multiple resource's slug</param>
 | 
				
			||||||
		/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
							/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
				
			||||||
		Task DeleteRange(params string[] slugs) => DeleteRange(slugs.AsEnumerable());
 | 
							Task DeleteRange(params string[] slugs) => DeleteRange(slugs.AsEnumerable());
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Delete a list of resources.
 | 
							/// Delete a list of resources.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="slugs">An enumerable of resources's slug</param>
 | 
							/// <param name="slugs">An enumerable of resource's slug</param>
 | 
				
			||||||
		/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
							/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
				
			||||||
		Task DeleteRange(IEnumerable<string> slugs);
 | 
							Task DeleteRange(IEnumerable<string> slugs);
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
@ -294,7 +294,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
	public interface IShowRepository : IRepository<Show>
 | 
						public interface IShowRepository : IRepository<Show>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Link a show to a collection and/or a library. The given show is now part of thoses containers.
 | 
							/// Link a show to a collection and/or a library. The given show is now part of those containers.
 | 
				
			||||||
		/// If both a library and a collection are given, the collection is added to the library too.
 | 
							/// If both a library and a collection are given, the collection is added to the library too.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="showID">The ID of the show</param>
 | 
							/// <param name="showID">The ID of the show</param>
 | 
				
			||||||
@ -421,7 +421,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// <param name="slug">The slug of the track</param>
 | 
							/// <param name="slug">The slug of the track</param>
 | 
				
			||||||
		/// <param name="type">The type (Video, Audio or Subtitle)</param>
 | 
							/// <param name="type">The type (Video, Audio or Subtitle)</param>
 | 
				
			||||||
		/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
							/// <exception cref="ItemNotFoundException">If the item is not found</exception>
 | 
				
			||||||
		/// <returns>The tracl found</returns>
 | 
							/// <returns>The track found</returns>
 | 
				
			||||||
		Task<Track> Get(string slug, StreamType type = StreamType.Unknown);
 | 
							Task<Track> Get(string slug, StreamType type = StreamType.Unknown);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
@ -429,7 +429,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="slug">The slug of the track</param>
 | 
							/// <param name="slug">The slug of the track</param>
 | 
				
			||||||
		/// <param name="type">The type (Video, Audio or Subtitle)</param>
 | 
							/// <param name="type">The type (Video, Audio or Subtitle)</param>
 | 
				
			||||||
		/// <returns>The tracl found</returns>
 | 
							/// <returns>The track found</returns>
 | 
				
			||||||
		Task<Track> GetOrDefault(string slug, StreamType type = StreamType.Unknown);
 | 
							Task<Track> GetOrDefault(string slug, StreamType type = StreamType.Unknown);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
@ -439,16 +439,16 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
	public interface ILibraryRepository : IRepository<Library> { }
 | 
						public interface ILibraryRepository : IRepository<Library> { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/// <summary>
 | 
						/// <summary>
 | 
				
			||||||
	/// A repository to handle library items (A wrapper arround shows and collections).
 | 
						/// A repository to handle library items (A wrapper around shows and collections).
 | 
				
			||||||
	/// </summary>
 | 
						/// </summary>
 | 
				
			||||||
	public interface ILibraryItemRepository : IRepository<LibraryItem>
 | 
						public interface ILibraryItemRepository : IRepository<LibraryItem>
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Get items (A wrapper arround shows or collections) from a library.
 | 
							/// Get items (A wrapper around shows or collections) from a library.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="id">The ID of the library</param>
 | 
							/// <param name="id">The ID of the library</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
		/// <param name="sort">Sort informations (sort order & sort by)</param>
 | 
							/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">How many items to return and where to start</param>
 | 
							/// <param name="limit">How many items to return and where to start</param>
 | 
				
			||||||
		/// <returns>A list of items that match every filters</returns>
 | 
							/// <returns>A list of items that match every filters</returns>
 | 
				
			||||||
		public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
 | 
							public Task<ICollection<LibraryItem>> GetFromLibrary(int id,
 | 
				
			||||||
@ -456,7 +456,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
			Sort<LibraryItem> sort = default,
 | 
								Sort<LibraryItem> sort = default,
 | 
				
			||||||
			Pagination limit = default);
 | 
								Pagination limit = default);
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Get items (A wrapper arround shows or collections) from a library.
 | 
							/// Get items (A wrapper around shows or collections) from a library.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="id">The ID of the library</param>
 | 
							/// <param name="id">The ID of the library</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
@ -470,11 +470,11 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		) => GetFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
 | 
							) => GetFromLibrary(id, where, new Sort<LibraryItem>(sort), limit);
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Get items (A wrapper arround shows or collections) from a library.
 | 
							/// Get items (A wrapper around shows or collections) from a library.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="slug">The slug of the library</param>
 | 
							/// <param name="slug">The slug of the library</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
		/// <param name="sort">Sort informations (sort order & sort by)</param>
 | 
							/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">How many items to return and where to start</param>
 | 
							/// <param name="limit">How many items to return and where to start</param>
 | 
				
			||||||
		/// <returns>A list of items that match every filters</returns>
 | 
							/// <returns>A list of items that match every filters</returns>
 | 
				
			||||||
		public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
 | 
							public Task<ICollection<LibraryItem>> GetFromLibrary(string slug,
 | 
				
			||||||
@ -482,7 +482,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
			Sort<LibraryItem> sort = default,
 | 
								Sort<LibraryItem> sort = default,
 | 
				
			||||||
			Pagination limit = default);
 | 
								Pagination limit = default);
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// Get items (A wrapper arround shows or collections) from a library.
 | 
							/// Get items (A wrapper around shows or collections) from a library.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="slug">The slug of the library</param>
 | 
							/// <param name="slug">The slug of the library</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
@ -521,7 +521,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="showID">The ID of the show</param>
 | 
							/// <param name="showID">The ID of the show</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
		/// <param name="sort">Sort informations (sort order & sort by)</param>
 | 
							/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">How many items to return and where to start</param>
 | 
							/// <param name="limit">How many items to return and where to start</param>
 | 
				
			||||||
		/// <returns>A list of items that match every filters</returns>
 | 
							/// <returns>A list of items that match every filters</returns>
 | 
				
			||||||
		Task<ICollection<PeopleRole>> GetFromShow(int showID,
 | 
							Task<ICollection<PeopleRole>> GetFromShow(int showID,
 | 
				
			||||||
@ -547,7 +547,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="showSlug">The slug of the show</param>
 | 
							/// <param name="showSlug">The slug of the show</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
		/// <param name="sort">Sort informations (sort order & sort by)</param>
 | 
							/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">How many items to return and where to start</param>
 | 
							/// <param name="limit">How many items to return and where to start</param>
 | 
				
			||||||
		/// <returns>A list of items that match every filters</returns>
 | 
							/// <returns>A list of items that match every filters</returns>
 | 
				
			||||||
		Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
 | 
							Task<ICollection<PeopleRole>> GetFromShow(string showSlug,
 | 
				
			||||||
@ -573,7 +573,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="id">The id of the person</param>
 | 
							/// <param name="id">The id of the person</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
		/// <param name="sort">Sort informations (sort order & sort by)</param>
 | 
							/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">How many items to return and where to start</param>
 | 
							/// <param name="limit">How many items to return and where to start</param>
 | 
				
			||||||
		/// <returns>A list of items that match every filters</returns>
 | 
							/// <returns>A list of items that match every filters</returns>
 | 
				
			||||||
		Task<ICollection<PeopleRole>> GetFromPeople(int id,
 | 
							Task<ICollection<PeopleRole>> GetFromPeople(int id,
 | 
				
			||||||
@ -599,7 +599,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="slug">The slug of the person</param>
 | 
							/// <param name="slug">The slug of the person</param>
 | 
				
			||||||
		/// <param name="where">A filter function</param>
 | 
							/// <param name="where">A filter function</param>
 | 
				
			||||||
		/// <param name="sort">Sort informations (sort order & sort by)</param>
 | 
							/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">How many items to return and where to start</param>
 | 
							/// <param name="limit">How many items to return and where to start</param>
 | 
				
			||||||
		/// <returns>A list of items that match every filters</returns>
 | 
							/// <returns>A list of items that match every filters</returns>
 | 
				
			||||||
		Task<ICollection<PeopleRole>> GetFromPeople(string slug,
 | 
							Task<ICollection<PeopleRole>> GetFromPeople(string slug,
 | 
				
			||||||
@ -631,7 +631,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="where">A predicate to add arbitrary filter</param>
 | 
							/// <param name="where">A predicate to add arbitrary filter</param>
 | 
				
			||||||
		/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
							/// <param name="sort">Sort information (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">Paginations information (where to start and how many to get)</param>
 | 
							/// <param name="limit">Pagination information (where to start and how many to get)</param>
 | 
				
			||||||
		/// <returns>A filtered list of external ids.</returns>
 | 
							/// <returns>A filtered list of external ids.</returns>
 | 
				
			||||||
		Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null, 
 | 
							Task<ICollection<MetadataID>> GetMetadataID(Expression<Func<MetadataID, bool>> where = null, 
 | 
				
			||||||
			Sort<MetadataID> sort = default,
 | 
								Sort<MetadataID> sort = default,
 | 
				
			||||||
@ -642,11 +642,16 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="where">A predicate to add arbitrary filter</param>
 | 
							/// <param name="where">A predicate to add arbitrary filter</param>
 | 
				
			||||||
		/// <param name="sort">A sort by expression</param>
 | 
							/// <param name="sort">A sort by expression</param>
 | 
				
			||||||
		/// <param name="limit">Paginations information (where to start and how many to get)</param>
 | 
							/// <param name="limit">Pagination information (where to start and how many to get)</param>
 | 
				
			||||||
		/// <returns>A filtered list of external ids.</returns>
 | 
							/// <returns>A filtered list of external ids.</returns>
 | 
				
			||||||
		Task<ICollection<MetadataID>> GetMetadataID([Optional] Expression<Func<MetadataID, bool>> where,
 | 
							Task<ICollection<MetadataID>> GetMetadataID([Optional] Expression<Func<MetadataID, bool>> where,
 | 
				
			||||||
			Expression<Func<MetadataID, object>> sort,
 | 
								Expression<Func<MetadataID, object>> sort,
 | 
				
			||||||
			Pagination limit = default
 | 
								Pagination limit = default
 | 
				
			||||||
		) => GetMetadataID(where, new Sort<MetadataID>(sort), limit);
 | 
							) => GetMetadataID(where, new Sort<MetadataID>(sort), limit);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// A repository to handle users.
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public interface IUserRepository : IRepository<User> {}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -28,6 +28,16 @@ namespace Kyoo.Models
 | 
				
			|||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public string Password { get; set; }
 | 
							public string Password { get; set; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The list of permissions of the user. The format of this is implementation dependent.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public string[] Permissions { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Arbitrary extra data that can be used by specific authentication implementations.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public Dictionary<string, string> ExtraData { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// The list of shows the user has finished.
 | 
							/// The list of shows the user has finished.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
@ -36,6 +46,18 @@ namespace Kyoo.Models
 | 
				
			|||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// The list of episodes the user is watching (stopped in progress or the next episode of the show)
 | 
							/// The list of episodes the user is watching (stopped in progress or the next episode of the show)
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		public ICollection<(Episode episode, int watchedPercentage)> CurrentlyWatching { get; set; }
 | 
							public ICollection<WatchedEpisode> CurrentlyWatching { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
					#if ENABLE_INTERNAL_LINKS
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Links between Users and Shows.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public ICollection<Link<User, Show>> ShowLinks { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Links between Users and WatchedEpisodes.
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public ICollection<Link<User, WatchedEpisode>> EpisodeLinks { get; set; }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										30
									
								
								Kyoo.Common/Models/WatchedEpisode.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								Kyoo.Common/Models/WatchedEpisode.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
				
			|||||||
 | 
					using Kyoo.Models.Attributes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Kyoo.Models
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// Metadata of episode currently watching by an user
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public class WatchedEpisode : IResource
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							[SerializeIgnore] public int ID
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								get => Episode.ID;
 | 
				
			||||||
 | 
								set => Episode.ID = value;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							[SerializeIgnore] public string Slug => Episode.Slug;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The episode currently watched
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public Episode Episode { get; set; }
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Where the player has stopped watching the episode (-1 if not started, else between 0 and 100).
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							public int WatchedPercentage { get; set; }
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -215,7 +215,7 @@ namespace Kyoo
 | 
				
			|||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// An advanced <see cref="Complete{T}"/> function.
 | 
							/// An advanced <see cref="Complete{T}"/> function.
 | 
				
			||||||
		/// This will set missing values of <see cref="first"/> to the corresponding values of <see cref="second"/>.
 | 
							/// This will set missing values of <see cref="first"/> to the corresponding values of <see cref="second"/>.
 | 
				
			||||||
		/// Enumerables will be merged (concatened).
 | 
							/// Enumerable will be merged (concatenated).
 | 
				
			||||||
		/// At the end, the OnMerge method of first will be called if first is a <see cref="IOnMerge"/>.
 | 
							/// At the end, the OnMerge method of first will be called if first is a <see cref="IOnMerge"/>.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="first">The object to complete</param>
 | 
							/// <param name="first">The object to complete</param>
 | 
				
			||||||
 | 
				
			|||||||
@ -67,7 +67,7 @@ namespace Kyoo
 | 
				
			|||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// The list of registered users.
 | 
							/// The list of registered users.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		// public DbSet<User> Users { get; set; }
 | 
							public DbSet<User> Users { get; set; }
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// All people's role. See <see cref="PeopleRole"/>.
 | 
							/// All people's role. See <see cref="PeopleRole"/>.
 | 
				
			||||||
@ -254,7 +254,7 @@ namespace Kyoo
 | 
				
			|||||||
		/// Return a new or an in cache temporary object wih the same ID as the one given
 | 
							/// Return a new or an in cache temporary object wih the same ID as the one given
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="model">If a resource with the same ID is found in the database, it will be used.
 | 
							/// <param name="model">If a resource with the same ID is found in the database, it will be used.
 | 
				
			||||||
		/// <see cref="model"/> will be used overwise</param>
 | 
							/// <see cref="model"/> will be used otherwise</param>
 | 
				
			||||||
		/// <typeparam name="T">The type of the resource</typeparam>
 | 
							/// <typeparam name="T">The type of the resource</typeparam>
 | 
				
			||||||
		/// <returns>A resource that is now tracked by this context.</returns>
 | 
							/// <returns>A resource that is now tracked by this context.</returns>
 | 
				
			||||||
		public T GetTemporaryObject<T>(T model)
 | 
							public T GetTemporaryObject<T>(T model)
 | 
				
			||||||
 | 
				
			|||||||
@ -118,7 +118,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// <param name="query">The base query to filter.</param>
 | 
							/// <param name="query">The base query to filter.</param>
 | 
				
			||||||
		/// <param name="where">An expression to filter based on arbitrary conditions</param>
 | 
							/// <param name="where">An expression to filter based on arbitrary conditions</param>
 | 
				
			||||||
		/// <param name="sort">The sort settings (sort order & sort by)</param>
 | 
							/// <param name="sort">The sort settings (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">Paginations information (where to start and how many to get)</param>
 | 
							/// <param name="limit">Pagination information (where to start and how many to get)</param>
 | 
				
			||||||
		/// <returns>The filtered query</returns>
 | 
							/// <returns>The filtered query</returns>
 | 
				
			||||||
		protected Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
 | 
							protected Task<ICollection<T>> ApplyFilters(IQueryable<T> query,
 | 
				
			||||||
			Expression<Func<T, bool>> where = null,
 | 
								Expression<Func<T, bool>> where = null,
 | 
				
			||||||
@ -137,7 +137,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		/// <param name="query">The base query to filter.</param>
 | 
							/// <param name="query">The base query to filter.</param>
 | 
				
			||||||
		/// <param name="where">An expression to filter based on arbitrary conditions</param>
 | 
							/// <param name="where">An expression to filter based on arbitrary conditions</param>
 | 
				
			||||||
		/// <param name="sort">The sort settings (sort order & sort by)</param>
 | 
							/// <param name="sort">The sort settings (sort order & sort by)</param>
 | 
				
			||||||
		/// <param name="limit">Paginations information (where to start and how many to get)</param>
 | 
							/// <param name="limit">Pagination information (where to start and how many to get)</param>
 | 
				
			||||||
		/// <returns>The filtered query</returns>
 | 
							/// <returns>The filtered query</returns>
 | 
				
			||||||
		protected async Task<ICollection<TValue>> ApplyFilters<TValue>(IQueryable<TValue> query,
 | 
							protected async Task<ICollection<TValue>> ApplyFilters<TValue>(IQueryable<TValue> query,
 | 
				
			||||||
			Func<int, Task<TValue>> get,
 | 
								Func<int, Task<TValue>> get,
 | 
				
			||||||
@ -244,7 +244,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		/// <summary>
 | 
							/// <summary>
 | 
				
			||||||
		/// An overridable method to edit relatiosn of a resource.
 | 
							/// An overridable method to edit relation of a resource.
 | 
				
			||||||
		/// </summary>
 | 
							/// </summary>
 | 
				
			||||||
		/// <param name="resource">The non edited resource</param>
 | 
							/// <param name="resource">The non edited resource</param>
 | 
				
			||||||
		/// <param name="changed">The new version of <see cref="resource"/>. This item will be saved on the databse and replace <see cref="resource"/></param>
 | 
							/// <param name="changed">The new version of <see cref="resource"/>. This item will be saved on the databse and replace <see cref="resource"/></param>
 | 
				
			||||||
 | 
				
			|||||||
@ -33,7 +33,7 @@ namespace Kyoo.Controllers
 | 
				
			|||||||
				} catch (Exception ex) 
 | 
									} catch (Exception ex) 
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					await Console.Error.WriteLineAsync(
 | 
										await Console.Error.WriteLineAsync(
 | 
				
			||||||
						$"The provider {provider.Provider.Name} coudln't work for {what}. Exception: {ex.Message}");
 | 
											$"The provider {provider.Provider.Name} could not work for {what}. Exception: {ex.Message}");
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return ret;
 | 
								return ret;
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										55
									
								
								Kyoo/Controllers/Repositories/UserRepository.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								Kyoo/Controllers/Repositories/UserRepository.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,55 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					using System.Collections.Generic;
 | 
				
			||||||
 | 
					using System.Linq;
 | 
				
			||||||
 | 
					using System.Linq.Expressions;
 | 
				
			||||||
 | 
					using System.Threading.Tasks;
 | 
				
			||||||
 | 
					using Kyoo.Models;
 | 
				
			||||||
 | 
					using Microsoft.EntityFrameworkCore;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Kyoo.Controllers
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/// <summary>
 | 
				
			||||||
 | 
						/// A repository for users.
 | 
				
			||||||
 | 
						/// </summary>
 | 
				
			||||||
 | 
						public class UserRepository : LocalRepository<User>, IUserRepository
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// The database handle
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							private readonly DatabaseContext _database;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							protected override Expression<Func<User, object>> DefaultSort => x => x.Username;
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <summary>
 | 
				
			||||||
 | 
							/// Create a new <see cref="UserRepository"/>
 | 
				
			||||||
 | 
							/// </summary>
 | 
				
			||||||
 | 
							/// <param name="database">The database handle to use</param>
 | 
				
			||||||
 | 
							public UserRepository(DatabaseContext database)
 | 
				
			||||||
 | 
								: base(database)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								_database = database;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public override async Task<ICollection<User>> Search(string query)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								return await _database.Users
 | 
				
			||||||
 | 
									.Where(_database.Like<User>(x => x.Username, $"%{query}%"))
 | 
				
			||||||
 | 
									.OrderBy(DefaultSort)
 | 
				
			||||||
 | 
									.Take(20)
 | 
				
			||||||
 | 
									.ToListAsync();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/// <inheritdoc />
 | 
				
			||||||
 | 
							public override async Task Delete(User obj)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								if (obj == null)
 | 
				
			||||||
 | 
									throw new ArgumentNullException(nameof(obj));
 | 
				
			||||||
 | 
								
 | 
				
			||||||
 | 
								_database.Entry(obj).State = EntityState.Deleted;
 | 
				
			||||||
 | 
								await _database.SaveChangesAsync();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user