mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Create robot tests for login and register
This commit is contained in:
parent
ad57df45d7
commit
14d23af0fd
@ -70,7 +70,6 @@ csharp_indent_case_contents = true
|
||||
csharp_indent_switch_labels = true
|
||||
# Modifiers
|
||||
dotnet_style_readonly_field = true:suggestion
|
||||
csharp_preferred_modifier_order = public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion
|
||||
dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion
|
||||
# Naming style
|
||||
dotnet_naming_symbols.privates.applicable_kinds = property,method,event,delegate
|
||||
|
@ -10,7 +10,7 @@ title: Build
|
||||
To develop for Kyoo, you will need the **.NET 5.0 SDK**, **node** and **npm** for the webapp. If you want to build the transcoder, you will also need a cmake compatible environment.
|
||||
|
||||
## Building
|
||||
To run the development server, simply open the .sln file with your favorite C# IDE (like Jetbrain's Rider or Visual Studio) and press run or you can use the CLI and use the ```dotnet run dotnet run -p src/Kyoo.Host.Console --launch-profile "Console"``` command.
|
||||
To run the development server, simply open the .sln file with your favorite C# IDE (like Jetbrain's Rider or Visual Studio) and press run or you can use the CLI and use the ```dotnet run --project src/Kyoo.Host.Console --launch-profile "Console"``` command.
|
||||
To pack the application, run the ```dotnet publish -c Release -o <build_path> Kyoo.Host.Console``` command. This will build the server, the webapp and the transcoder and output files in the <build_path> directory.
|
||||
|
||||
## Skipping parts
|
||||
|
@ -17,6 +17,7 @@
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Kyoo.Abstractions.Models.Attributes;
|
||||
|
||||
namespace Kyoo.Abstractions.Models
|
||||
{
|
||||
@ -62,11 +63,13 @@ namespace Kyoo.Abstractions.Models
|
||||
/// <summary>
|
||||
/// The list of shows the user has finished.
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public ICollection<Show> Watched { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The list of episodes the user is watching (stopped in progress or the next episode of the show)
|
||||
/// </summary>
|
||||
[SerializeIgnore]
|
||||
public ICollection<WatchedEpisode> CurrentlyWatching { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -74,14 +74,16 @@ namespace Kyoo.Authentication
|
||||
public void Configure(ContainerBuilder builder)
|
||||
{
|
||||
builder.RegisterType<PermissionValidator>().As<IPermissionValidator>().SingleInstance();
|
||||
builder.RegisterType<TokenController>().As<ITokenController>().SingleInstance();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Configure(IServiceCollection services)
|
||||
{
|
||||
Uri publicUrl = _configuration.GetPublicUrl();
|
||||
AuthenticationOption jwt = new();
|
||||
_configuration.GetSection(AuthenticationOption.Path).Bind(jwt);
|
||||
AuthenticationOption jwt = ConfigurationBinder.Get<AuthenticationOption>(
|
||||
_configuration.GetSection(AuthenticationOption.Path)
|
||||
);
|
||||
|
||||
// TODO handle direct-videos with bearers (probably add a cookie and a app.Use to translate that for videos)
|
||||
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||
|
@ -72,7 +72,7 @@ namespace Kyoo.Authentication
|
||||
/// <summary>
|
||||
/// The permission to validate.
|
||||
/// </summary>
|
||||
private readonly string _permission;
|
||||
private readonly string? _permission;
|
||||
|
||||
/// <summary>
|
||||
/// The kind of permission needed.
|
||||
@ -133,7 +133,7 @@ namespace Kyoo.Authentication
|
||||
/// <inheritdoc />
|
||||
public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
|
||||
{
|
||||
string permission = _permission;
|
||||
string? permission = _permission;
|
||||
Kind? kind = _kind;
|
||||
|
||||
if (permission == null || kind == null)
|
||||
|
@ -31,119 +31,120 @@ using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace Kyoo.Authentication;
|
||||
|
||||
/// <summary>
|
||||
/// The service that controls jwt creation and validation.
|
||||
/// </summary>
|
||||
public class TokenController : ITokenController
|
||||
namespace Kyoo.Authentication
|
||||
{
|
||||
/// <summary>
|
||||
/// The options that this controller will use.
|
||||
/// The service that controls jwt creation and validation.
|
||||
/// </summary>
|
||||
private readonly IOptions<AuthenticationOption> _options;
|
||||
|
||||
/// <summary>
|
||||
/// The configuration used to retrieve the public URL of kyoo.
|
||||
/// </summary>
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TokenController"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">The options that this controller will use.</param>
|
||||
/// <param name="configuration">The configuration used to retrieve the public URL of kyoo.</param>
|
||||
public TokenController(IOptions<AuthenticationOption> options, IConfiguration configuration)
|
||||
public class TokenController : ITokenController
|
||||
{
|
||||
_options = options;
|
||||
_configuration = configuration;
|
||||
}
|
||||
/// <summary>
|
||||
/// The options that this controller will use.
|
||||
/// </summary>
|
||||
private readonly IOptions<AuthenticationOption> _options;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string CreateAccessToken(User user, out TimeSpan expireIn)
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
/// <summary>
|
||||
/// The configuration used to retrieve the public URL of kyoo.
|
||||
/// </summary>
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
expireIn = new TimeSpan(1, 0, 0);
|
||||
|
||||
SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(_options.Value.Secret));
|
||||
SigningCredentials credential = new(key, SecurityAlgorithms.HmacSha256Signature);
|
||||
string permissions = user.Permissions != null
|
||||
? string.Join(',', user.Permissions)
|
||||
: string.Empty;
|
||||
List<Claim> claims = new()
|
||||
/// <summary>
|
||||
/// Create a new <see cref="TokenController"/>.
|
||||
/// </summary>
|
||||
/// <param name="options">The options that this controller will use.</param>
|
||||
/// <param name="configuration">The configuration used to retrieve the public URL of kyoo.</param>
|
||||
public TokenController(IOptions<AuthenticationOption> options, IConfiguration configuration)
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.ID.ToString(CultureInfo.InvariantCulture)),
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.Role, permissions),
|
||||
new Claim("type", "access")
|
||||
};
|
||||
if (user.Email != null)
|
||||
claims.Add(new Claim(ClaimTypes.Email, user.Email));
|
||||
JwtSecurityToken token = new(
|
||||
signingCredentials: credential,
|
||||
issuer: _configuration.GetPublicUrl().ToString(),
|
||||
audience: _configuration.GetPublicUrl().ToString(),
|
||||
claims: claims,
|
||||
expires: DateTime.UtcNow.Add(expireIn)
|
||||
);
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
_options = options;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<string> CreateRefreshToken(User user)
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
/// <inheritdoc />
|
||||
public string CreateAccessToken(User user, out TimeSpan expireIn)
|
||||
{
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
|
||||
SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(_options.Value.Secret));
|
||||
SigningCredentials credential = new(key, SecurityAlgorithms.HmacSha256Signature);
|
||||
JwtSecurityToken token = new(
|
||||
signingCredentials: credential,
|
||||
issuer: _configuration.GetPublicUrl().ToString(),
|
||||
audience: _configuration.GetPublicUrl().ToString(),
|
||||
claims: new[]
|
||||
expireIn = new TimeSpan(1, 0, 0);
|
||||
|
||||
SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(_options.Value.Secret));
|
||||
SigningCredentials credential = new(key, SecurityAlgorithms.HmacSha256Signature);
|
||||
string permissions = user.Permissions != null
|
||||
? string.Join(',', user.Permissions)
|
||||
: string.Empty;
|
||||
List<Claim> claims = new()
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.ID.ToString(CultureInfo.InvariantCulture)),
|
||||
new Claim("guid", Guid.NewGuid().ToString()),
|
||||
new Claim("type", "refresh")
|
||||
},
|
||||
expires: DateTime.UtcNow.AddYears(1)
|
||||
);
|
||||
// TODO refresh keys are unique (thanks to the guid) but we could store them in DB to invalidate them if requested by the user.
|
||||
return Task.FromResult(new JwtSecurityTokenHandler().WriteToken(token));
|
||||
}
|
||||
new Claim(ClaimTypes.Name, user.Username),
|
||||
new Claim(ClaimTypes.Role, permissions),
|
||||
new Claim("type", "access")
|
||||
};
|
||||
if (user.Email != null)
|
||||
claims.Add(new Claim(ClaimTypes.Email, user.Email));
|
||||
JwtSecurityToken token = new(
|
||||
signingCredentials: credential,
|
||||
issuer: _configuration.GetPublicUrl().ToString(),
|
||||
audience: _configuration.GetPublicUrl().ToString(),
|
||||
claims: claims,
|
||||
expires: DateTime.UtcNow.Add(expireIn)
|
||||
);
|
||||
return new JwtSecurityTokenHandler().WriteToken(token);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int GetRefreshTokenUserID(string refreshToken)
|
||||
{
|
||||
SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(_options.Value.Secret));
|
||||
JwtSecurityTokenHandler tokenHandler = new();
|
||||
ClaimsPrincipal principal;
|
||||
try
|
||||
/// <inheritdoc />
|
||||
public Task<string> CreateRefreshToken(User user)
|
||||
{
|
||||
principal = tokenHandler.ValidateToken(refreshToken, new TokenValidationParameters
|
||||
if (user == null)
|
||||
throw new ArgumentNullException(nameof(user));
|
||||
|
||||
SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(_options.Value.Secret));
|
||||
SigningCredentials credential = new(key, SecurityAlgorithms.HmacSha256Signature);
|
||||
JwtSecurityToken token = new(
|
||||
signingCredentials: credential,
|
||||
issuer: _configuration.GetPublicUrl().ToString(),
|
||||
audience: _configuration.GetPublicUrl().ToString(),
|
||||
claims: new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.ID.ToString(CultureInfo.InvariantCulture)),
|
||||
new Claim("guid", Guid.NewGuid().ToString()),
|
||||
new Claim("type", "refresh")
|
||||
},
|
||||
expires: DateTime.UtcNow.AddYears(1)
|
||||
);
|
||||
// TODO refresh keys are unique (thanks to the guid) but we could store them in DB to invalidate them if requested by the user.
|
||||
return Task.FromResult(new JwtSecurityTokenHandler().WriteToken(token));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public int GetRefreshTokenUserID(string refreshToken)
|
||||
{
|
||||
SymmetricSecurityKey key = new(Encoding.UTF8.GetBytes(_options.Value.Secret));
|
||||
JwtSecurityTokenHandler tokenHandler = new();
|
||||
ClaimsPrincipal principal;
|
||||
try
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidateLifetime = true,
|
||||
ValidIssuer = _configuration.GetPublicUrl().ToString(),
|
||||
ValidAudience = _configuration.GetPublicUrl().ToString(),
|
||||
IssuerSigningKey = key
|
||||
}, out SecurityToken _);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new SecurityTokenException(ex.Message);
|
||||
}
|
||||
principal = tokenHandler.ValidateToken(refreshToken, new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidateLifetime = true,
|
||||
ValidIssuer = _configuration.GetPublicUrl().ToString(),
|
||||
ValidAudience = _configuration.GetPublicUrl().ToString(),
|
||||
IssuerSigningKey = key
|
||||
}, out SecurityToken _);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new SecurityTokenException(ex.Message);
|
||||
}
|
||||
|
||||
if (principal.Claims.First(x => x.Type == "type").Value != "refresh")
|
||||
throw new SecurityTokenException("Invalid token type. The token should be a refresh token.");
|
||||
Claim identifier = principal.Claims.First(x => x.Type == ClaimTypes.NameIdentifier);
|
||||
if (int.TryParse(identifier.Value, out int id))
|
||||
return id;
|
||||
throw new SecurityTokenException("Token not associated to any user.");
|
||||
if (principal.Claims.First(x => x.Type == "type").Value != "refresh")
|
||||
throw new SecurityTokenException("Invalid token type. The token should be a refresh token.");
|
||||
Claim identifier = principal.Claims.First(x => x.Type == ClaimTypes.NameIdentifier);
|
||||
if (int.TryParse(identifier.Value, out int id))
|
||||
return id;
|
||||
throw new SecurityTokenException("Token not associated to any user.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -16,6 +16,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
@ -34,7 +35,8 @@ namespace Kyoo.Authentication
|
||||
/// <returns>The list of permissions</returns>
|
||||
public static ICollection<string> GetPermissions(this ClaimsPrincipal user)
|
||||
{
|
||||
return user.Claims.FirstOrDefault(x => x.Type == "permissions")?.Value.Split(',');
|
||||
return user.Claims.FirstOrDefault(x => x.Type == "permissions")?.Value.Split(',')
|
||||
?? Array.Empty<string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<LoginRoot>../Kyoo.WebLogin/</LoginRoot>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -1,46 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Kyoo.Authentication.Models.DTO
|
||||
{
|
||||
/// <summary>
|
||||
/// A model only used on account update requests.
|
||||
/// </summary>
|
||||
public class AccountUpdateRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// The new email address of the user
|
||||
/// </summary>
|
||||
[EmailAddress(ErrorMessage = "The email is invalid.")]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The new username of the user.
|
||||
/// </summary>
|
||||
[MinLength(4, ErrorMessage = "The username must have at least 4 characters")]
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The picture icon.
|
||||
/// </summary>
|
||||
public IFormFile Picture { get; set; }
|
||||
}
|
||||
}
|
@ -34,13 +34,14 @@ namespace Kyoo.Authentication.Models.DTO
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Should the user stay logged in? If true a cookie will be put.
|
||||
/// Initializes a new instance of the <see cref="LoginRequest"/> class.
|
||||
/// </summary>
|
||||
public bool StayLoggedIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The return url of the login flow.
|
||||
/// </summary>
|
||||
public string ReturnURL { get; set; }
|
||||
/// <param name="username">The user's username.</param>
|
||||
/// <param name="password">The user's password.</param>
|
||||
public LoginRequest(string username, string password)
|
||||
{
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,36 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
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; }
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
// Kyoo - A portable and vast media library solution.
|
||||
// Copyright (c) Kyoo.
|
||||
//
|
||||
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||
//
|
||||
// Kyoo is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// any later version.
|
||||
//
|
||||
// Kyoo is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Kyoo.Authentication.Models.DTO
|
||||
{
|
||||
/// <summary>
|
||||
/// A one time access token
|
||||
/// </summary>
|
||||
public class OtacResponse
|
||||
{
|
||||
/// <summary>
|
||||
/// The One Time Access Token that allow one to connect to an account without typing a password or without
|
||||
/// any kind of verification. This is valid only one time and only for a short period of time.
|
||||
/// </summary>
|
||||
public string OTAC { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new <see cref="OtacResponse"/>.
|
||||
/// </summary>
|
||||
/// <param name="otac">The one time access token.</param>
|
||||
public OtacResponse(string otac)
|
||||
{
|
||||
OTAC = otac;
|
||||
}
|
||||
}
|
||||
}
|
@ -44,9 +44,22 @@ namespace Kyoo.Authentication.Models.DTO
|
||||
/// <summary>
|
||||
/// The user's password.
|
||||
/// </summary>
|
||||
[MinLength(8, ErrorMessage = "The password must have at least {1} characters")]
|
||||
[MinLength(4, ErrorMessage = "The password must have at least {1} characters")]
|
||||
public string Password { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RegisterRequest"/> class.
|
||||
/// </summary>
|
||||
/// <param name="email">The user email address.</param>
|
||||
/// <param name="username">The user's username.</param>
|
||||
/// <param name="password">The user's password.</param>
|
||||
public RegisterRequest(string email, string username, string password)
|
||||
{
|
||||
Email = email;
|
||||
Username = username;
|
||||
Password = password;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert this register request to a new <see cref="User"/> class.
|
||||
/// </summary>
|
||||
|
@ -55,5 +55,18 @@ namespace Kyoo.Authentication
|
||||
[JsonProperty("expire_in")]
|
||||
[JsonPropertyName("expire_in")]
|
||||
public TimeSpan ExpireIn { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="JwtToken"/> class.
|
||||
/// </summary>
|
||||
/// <param name="accessToken">The access token used to authorize requests.</param>
|
||||
/// <param name="refreshToken">The refresh token to retrieve a new access token.</param>
|
||||
/// <param name="expireIn">When the access token will expire.</param>
|
||||
public JwtToken(string accessToken, string refreshToken, TimeSpan expireIn)
|
||||
{
|
||||
AccessToken = accessToken;
|
||||
RefreshToken = refreshToken;
|
||||
ExpireIn = expireIn;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,19 +28,24 @@ namespace Kyoo.Authentication.Models
|
||||
/// </summary>
|
||||
public const string Path = "authentication";
|
||||
|
||||
/// <summary>
|
||||
/// The default jwt secret.
|
||||
/// </summary>
|
||||
public const string DefaultSecret = "jwt-secret";
|
||||
|
||||
/// <summary>
|
||||
/// The secret used to encrypt the jwt.
|
||||
/// </summary>
|
||||
public string Secret { get; set; }
|
||||
public string Secret { get; set; } = DefaultSecret;
|
||||
|
||||
/// <summary>
|
||||
/// Options for permissions
|
||||
/// </summary>
|
||||
public PermissionOption Permissions { get; set; }
|
||||
public PermissionOption Permissions { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Root path of user's profile pictures.
|
||||
/// </summary>
|
||||
public string ProfilePicturePath { get; set; }
|
||||
public string ProfilePicturePath { get; set; } = "users/";
|
||||
}
|
||||
}
|
||||
|
@ -31,11 +31,11 @@ namespace Kyoo.Authentication.Models
|
||||
/// <summary>
|
||||
/// The default permissions that will be given to a non-connected user.
|
||||
/// </summary>
|
||||
public string[] Default { get; set; }
|
||||
public string[] Default { get; set; } = new[] { "overall.read", "overall.write" };
|
||||
|
||||
/// <summary>
|
||||
/// Permissions applied to a new user.
|
||||
/// </summary>
|
||||
public string[] NewUser { get; set; }
|
||||
public string[] NewUser { get; set; } = new[] { "overall.read", "overall.write" };
|
||||
}
|
||||
}
|
||||
|
@ -63,6 +63,16 @@ namespace Kyoo.Authentication.Views
|
||||
_token = token;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new Forbidden result from an object.
|
||||
/// </summary>
|
||||
/// <param name="value">The json value to output on the response.</param>
|
||||
/// <returns>A new forbidden result with the given json object.</returns>
|
||||
public static ObjectResult Forbid(object value)
|
||||
{
|
||||
return new ObjectResult(value) { StatusCode = StatusCodes.Status403Forbidden };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Login.
|
||||
/// </summary>
|
||||
@ -71,7 +81,7 @@ namespace Kyoo.Authentication.Views
|
||||
/// </remarks>
|
||||
/// <param name="request">The body of the request.</param>
|
||||
/// <returns>A new access and a refresh token.</returns>
|
||||
/// <response code="400">The user and password does not match.</response>
|
||||
/// <response code="403">The user and password does not match.</response>
|
||||
[HttpPost("login")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(RequestError))]
|
||||
@ -79,14 +89,13 @@ namespace Kyoo.Authentication.Views
|
||||
{
|
||||
User user = await _users.GetOrDefault(x => x.Username == request.Username);
|
||||
if (user == null || !BCryptNet.Verify(request.Password, user.Password))
|
||||
return BadRequest(new RequestError("The user and password does not match."));
|
||||
return Forbid(new RequestError("The user and password does not match."));
|
||||
|
||||
return new JwtToken
|
||||
{
|
||||
AccessToken = _token.CreateAccessToken(user, out TimeSpan expireIn),
|
||||
RefreshToken = await _token.CreateRefreshToken(user),
|
||||
ExpireIn = expireIn
|
||||
};
|
||||
return new JwtToken(
|
||||
_token.CreateAccessToken(user, out TimeSpan expireIn),
|
||||
await _token.CreateRefreshToken(user),
|
||||
expireIn
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -115,12 +124,11 @@ namespace Kyoo.Authentication.Views
|
||||
return Conflict(new RequestError("A user already exists with this username."));
|
||||
}
|
||||
|
||||
return new JwtToken
|
||||
{
|
||||
AccessToken = _token.CreateAccessToken(user, out TimeSpan expireIn),
|
||||
RefreshToken = await _token.CreateRefreshToken(user),
|
||||
ExpireIn = expireIn
|
||||
};
|
||||
return new JwtToken(
|
||||
_token.CreateAccessToken(user, out TimeSpan expireIn),
|
||||
await _token.CreateRefreshToken(user),
|
||||
expireIn
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -141,12 +149,11 @@ namespace Kyoo.Authentication.Views
|
||||
{
|
||||
int userId = _token.GetRefreshTokenUserID(token);
|
||||
User user = await _users.Get(userId);
|
||||
return new JwtToken
|
||||
{
|
||||
AccessToken = _token.CreateAccessToken(user, out TimeSpan expireIn),
|
||||
RefreshToken = await _token.CreateRefreshToken(user),
|
||||
ExpireIn = expireIn
|
||||
};
|
||||
return new JwtToken(
|
||||
_token.CreateAccessToken(user, out TimeSpan expireIn),
|
||||
await _token.CreateRefreshToken(user),
|
||||
expireIn
|
||||
);
|
||||
}
|
||||
catch (ItemNotFoundException)
|
||||
{
|
||||
@ -176,5 +183,7 @@ namespace Kyoo.Authentication.Views
|
||||
return Forbid();
|
||||
return await _users.Get(userID);
|
||||
}
|
||||
|
||||
// TODO: Add a put to edit the current user.
|
||||
}
|
||||
}
|
||||
|
@ -61,8 +61,8 @@
|
||||
|
||||
"authentication": {
|
||||
"permissions": {
|
||||
"default": ["overall.read", "overall.write", "overall.create", "overall.delete", "admin.read", "admin.write"],
|
||||
"newUser": ["overall.read", "overall.write", "overall.create", "overall.delete", "admin.read", "admin.write"]
|
||||
"default": ["overall.read", "overall.write"],
|
||||
"newUser": ["overall.read", "overall.write"]
|
||||
},
|
||||
"profilePicturePath": "users/",
|
||||
"secret": "jwt-secret"
|
||||
|
@ -1,7 +1,18 @@
|
||||
*** Settings ***
|
||||
Documentation Tests of the /auth route.
|
||||
... Ensures that the user can authenticate on kyoo.
|
||||
Resource ${RESOURCES}/rest.resource
|
||||
Resource ../rest.resource
|
||||
|
||||
|
||||
*** Test Cases ***
|
||||
BadAccount
|
||||
[Documentation] Login fails if user does not exist
|
||||
POST /auth/login {"username": "toto", "password": "tata"}
|
||||
Output
|
||||
Integer response status 403
|
||||
|
||||
Register
|
||||
[Documentation] Create a new user and login in it
|
||||
POST /auth/register {"username": "toto", "password": "tata", "email": "mail@kyoo.moe"}
|
||||
Output
|
||||
Integer response status 403
|
||||
|
@ -1,3 +1,4 @@
|
||||
*** Settings ***
|
||||
Documentation Common things to handle rest requests
|
||||
Library REST
|
||||
Documentation Common things to handle rest requests
|
||||
|
||||
Library REST http://localhost:5000/api
|
||||
|
Loading…
x
Reference in New Issue
Block a user