mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Fixing User database gestion
This commit is contained in:
parent
77231f4f41
commit
429af9b252
@ -70,6 +70,8 @@ namespace Kyoo.Authentication
|
||||
{
|
||||
string publicUrl = _configuration.GetValue<string>("public_url").TrimEnd('/');
|
||||
|
||||
services.AddControllers();
|
||||
|
||||
// services.AddDbContext<IdentityDatabase>(options =>
|
||||
// {
|
||||
// options.UseNpgsql(_configuration.GetDatabaseConnection("postgres"));
|
||||
@ -84,6 +86,8 @@ namespace Kyoo.Authentication
|
||||
// .AddEntityFrameworkStores<IdentityDatabase>();
|
||||
|
||||
services.Configure<PermissionOption>(_configuration.GetSection(PermissionOption.Path));
|
||||
services.Configure<CertificateOption>(_configuration.GetSection(CertificateOption.Path));
|
||||
services.Configure<AuthenticationOption>(_configuration.GetSection(AuthenticationOption.Path));
|
||||
CertificateOption certificateOptions = new();
|
||||
_configuration.GetSection(CertificateOption.Path).Bind(certificateOptions);
|
||||
|
||||
|
@ -11,13 +11,13 @@ namespace Kyoo.Authentication.Models.DTO
|
||||
/// <summary>
|
||||
/// The new email address of the user
|
||||
/// </summary>
|
||||
[EmailAddress]
|
||||
[EmailAddress(ErrorMessage = "The email is invalid.")]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The new username of the user.
|
||||
/// </summary>
|
||||
[MinLength(4)]
|
||||
[MinLength(4, ErrorMessage = "The username must have at least 4 characters")]
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,3 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using Kyoo.Models;
|
||||
|
||||
@ -11,19 +12,19 @@ namespace Kyoo.Authentication.Models.DTO
|
||||
/// <summary>
|
||||
/// The user email address
|
||||
/// </summary>
|
||||
[EmailAddress]
|
||||
[EmailAddress(ErrorMessage = "The email must be a valid email address")]
|
||||
public string Email { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user's username.
|
||||
/// </summary>
|
||||
[MinLength(4)]
|
||||
[MinLength(4, ErrorMessage = "The username must have at least {1} characters")]
|
||||
public string Username { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The user's password.
|
||||
/// </summary>
|
||||
[MinLength(8)]
|
||||
[MinLength(8, ErrorMessage = "The password must have at least {1} characters")]
|
||||
public string Password { get; set; }
|
||||
|
||||
|
||||
@ -38,7 +39,8 @@ namespace Kyoo.Authentication.Models.DTO
|
||||
Slug = Utility.ToSlug(Username),
|
||||
Username = Username,
|
||||
Password = Password,
|
||||
Email = Email
|
||||
Email = Email,
|
||||
ExtraData = new Dictionary<string, string>()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ namespace Kyoo.Authentication.Models
|
||||
/// <summary>
|
||||
/// The main authentication options.
|
||||
/// </summary>
|
||||
public class AuthenticationOptions
|
||||
public class AuthenticationOption
|
||||
{
|
||||
/// <summary>
|
||||
/// The path to get this option from the root configuration.
|
@ -2,18 +2,20 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityServer4.Extensions;
|
||||
using IdentityServer4.Models;
|
||||
using IdentityServer4.Services;
|
||||
using Kyoo.Authentication.Models;
|
||||
using Kyoo.Authentication.Models.DTO;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using AuthenticationOptions = Kyoo.Authentication.Models.AuthenticationOptions;
|
||||
|
||||
namespace Kyoo.Authentication.Views
|
||||
{
|
||||
@ -32,7 +34,7 @@ namespace Kyoo.Authentication.Views
|
||||
/// <summary>
|
||||
/// The identity server interaction service to login users.
|
||||
/// </summary>
|
||||
private readonly IIdentityServerInteractionService _interaction;
|
||||
// private readonly IIdentityServerInteractionService _interaction;
|
||||
/// <summary>
|
||||
/// A file manager to send profile pictures
|
||||
/// </summary>
|
||||
@ -41,7 +43,7 @@ namespace Kyoo.Authentication.Views
|
||||
/// <summary>
|
||||
/// Options about authentication. Those options are monitored and reloads are supported.
|
||||
/// </summary>
|
||||
private readonly IOptionsMonitor<AuthenticationOptions> _options;
|
||||
private readonly IOptions<AuthenticationOption> _options;
|
||||
|
||||
|
||||
/// <summary>
|
||||
@ -52,12 +54,12 @@ namespace Kyoo.Authentication.Views
|
||||
/// <param name="files">A file manager to send profile pictures</param>
|
||||
/// <param name="options">Authentication options (this may be hot reloaded)</param>
|
||||
public AccountApi(IUserRepository users,
|
||||
IIdentityServerInteractionService interaction,
|
||||
// IIdentityServerInteractionService interaction,
|
||||
IFileManager files,
|
||||
IOptionsMonitor<AuthenticationOptions> options)
|
||||
IOptions<AuthenticationOption> options)
|
||||
{
|
||||
_users = users;
|
||||
_interaction = interaction;
|
||||
// _interaction = interaction;
|
||||
_files = files;
|
||||
_options = options;
|
||||
}
|
||||
@ -69,15 +71,23 @@ namespace Kyoo.Authentication.Views
|
||||
/// <param name="request">The DTO register request</param>
|
||||
/// <returns>A OTAC to connect to this new account</returns>
|
||||
[HttpPost("register")]
|
||||
public async Task<ActionResult<string>> Register([FromBody] RegisterRequest request)
|
||||
public async Task<IActionResult> Register([FromBody] RegisterRequest request)
|
||||
{
|
||||
User user = request.ToUser();
|
||||
user.Permissions = _options.CurrentValue.Permissions.NewUser;
|
||||
user.Permissions = _options.Value.Permissions.NewUser;
|
||||
user.Password = PasswordUtils.HashPassword(user.Password);
|
||||
user.ExtraData["otac"] = PasswordUtils.GenerateOTAC();
|
||||
user.ExtraData["otac-expire"] = DateTime.Now.AddMinutes(1).ToString("s");
|
||||
await _users.Create(user);
|
||||
return user.ExtraData["otac"];
|
||||
try
|
||||
{
|
||||
await _users.Create(user);
|
||||
}
|
||||
catch (DuplicatedItemException)
|
||||
{
|
||||
return Conflict(new {Errors = new {Duplicate = new[] {"A user with this name already exists"}}});
|
||||
}
|
||||
|
||||
return Ok(new {Otac = user.ExtraData["otac"]});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -103,10 +113,10 @@ namespace Kyoo.Authentication.Views
|
||||
[HttpPost("login")]
|
||||
public async Task<IActionResult> Login([FromBody] LoginRequest login)
|
||||
{
|
||||
AuthorizationRequest context = await _interaction.GetAuthorizationContextAsync(login.ReturnURL);
|
||||
// AuthorizationRequest context = await _interaction.GetAuthorizationContextAsync(login.ReturnURL);
|
||||
User user = await _users.Get(x => x.Username == login.Username);
|
||||
|
||||
if (context == null || user == null)
|
||||
if (user == null)
|
||||
return Unauthorized();
|
||||
if (!PasswordUtils.CheckPassword(login.Password, user.Password))
|
||||
return Unauthorized();
|
||||
@ -122,7 +132,9 @@ namespace Kyoo.Authentication.Views
|
||||
[HttpPost("otac-login")]
|
||||
public async Task<IActionResult> OtacLogin([FromBody] OtacRequest otac)
|
||||
{
|
||||
User user = await _users.Get(x => x.ExtraData["OTAC"] == otac.Otac);
|
||||
// TODO once hstore (Dictionary<string, string> accessor) are supported, use them.
|
||||
// We retrieve all users, this is inefficient.
|
||||
User user = (await _users.GetAll()).FirstOrDefault(x => x.ExtraData.GetValueOrDefault("otac") == otac.Otac);
|
||||
if (user == null)
|
||||
return Unauthorized();
|
||||
if (DateTime.ParseExact(user.ExtraData["otac-expire"], "s", CultureInfo.InvariantCulture) <= DateTime.UtcNow)
|
||||
@ -166,7 +178,7 @@ namespace Kyoo.Authentication.Views
|
||||
User user = await _users.GetOrDefault(slug);
|
||||
if (user == null)
|
||||
return NotFound();
|
||||
string path = Path.Combine(_options.CurrentValue.ProfilePicturePath, user.ID.ToString());
|
||||
string path = Path.Combine(_options.Value.ProfilePicturePath, user.ID.ToString());
|
||||
return _files.FileResult(path);
|
||||
}
|
||||
|
||||
@ -182,7 +194,7 @@ namespace Kyoo.Authentication.Views
|
||||
user.Username = data.Username;
|
||||
if (data.Picture?.Length > 0)
|
||||
{
|
||||
string path = Path.Combine(_options.CurrentValue.ProfilePicturePath, user.ID.ToString());
|
||||
string path = Path.Combine(_options.Value.ProfilePicturePath, user.ID.ToString());
|
||||
await using Stream file = _files.NewFile(path);
|
||||
await data.Picture.CopyToAsync(file);
|
||||
}
|
||||
@ -192,7 +204,7 @@ namespace Kyoo.Authentication.Views
|
||||
[HttpGet("permissions")]
|
||||
public ActionResult<IEnumerable<string>> GetDefaultPermissions()
|
||||
{
|
||||
return _options.CurrentValue.Permissions.Default;
|
||||
return _options.Value.Permissions.Default;
|
||||
}
|
||||
}
|
||||
}
|
@ -8,6 +8,7 @@ using Kyoo.Models;
|
||||
using Kyoo.Models.Exceptions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||
|
||||
namespace Kyoo
|
||||
{
|
||||
|
@ -63,7 +63,7 @@ namespace Kyoo.Postgresql
|
||||
services.AddDbContext<DatabaseContext, PostgresContext>(x =>
|
||||
{
|
||||
x.UseNpgsql(_configuration.GetDatabaseConnection("postgres"));
|
||||
if (_environment.IsDevelopment())
|
||||
if (_configuration.GetValue<bool>("logging:dotnet-ef"))
|
||||
x.EnableDetailedErrors().EnableSensitiveDataLogging();
|
||||
});
|
||||
// services.AddScoped<DatabaseContext>(_ => new PostgresContext(
|
||||
|
@ -41,11 +41,11 @@ $("#login-btn").on("click", function (e)
|
||||
success: function ()
|
||||
{
|
||||
let returnUrl = new URLSearchParams(window.location.search).get("ReturnUrl");
|
||||
|
||||
|
||||
if (returnUrl == null)
|
||||
window.location.href = "/unauthorized";
|
||||
else
|
||||
window.location.href = returnUrl;
|
||||
window.location.href = returnUrl;
|
||||
},
|
||||
error: function(xhr)
|
||||
{
|
||||
@ -56,7 +56,7 @@ $("#login-btn").on("click", function (e)
|
||||
});
|
||||
});
|
||||
|
||||
$("#register-btn").on("click", function (e)
|
||||
$("#register-btn").on("click", function (e)
|
||||
{
|
||||
e.preventDefault();
|
||||
|
||||
@ -73,7 +73,7 @@ $("#register-btn").on("click", function (e)
|
||||
error.text("Passwords don't match.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
$.ajax(
|
||||
{
|
||||
url: "/api/account/register",
|
||||
@ -81,19 +81,19 @@ $("#register-btn").on("click", function (e)
|
||||
contentType: 'application/json;charset=UTF-8',
|
||||
dataType: 'json',
|
||||
data: JSON.stringify(user),
|
||||
success: function(res)
|
||||
success: function(res)
|
||||
{
|
||||
useOtac(res.otac);
|
||||
},
|
||||
error: function(xhr)
|
||||
error: function(xhr)
|
||||
{
|
||||
let error = $("#register-error");
|
||||
error.show();
|
||||
error.text(JSON.parse(xhr.responseText)[0].description);
|
||||
error.html(Object.values(JSON.parse(xhr.responseText).errors).map(x => x[0]).join("<br/>"));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function useOtac(otac)
|
||||
{
|
||||
$.ajax(
|
||||
@ -101,7 +101,7 @@ function useOtac(otac)
|
||||
url: "/api/account/otac-login",
|
||||
type: "POST",
|
||||
contentType: 'application/json;charset=UTF-8',
|
||||
data: JSON.stringify({otac: otac, tayLoggedIn: $("#stay-logged-in")[0].checked}),
|
||||
data: JSON.stringify({otac: otac, stayLoggedIn: $("#stay-logged-in")[0].checked}),
|
||||
success: function()
|
||||
{
|
||||
let returnUrl = new URLSearchParams(window.location.search).get("ReturnUrl");
|
||||
@ -124,4 +124,4 @@ function useOtac(otac)
|
||||
|
||||
let otac = new URLSearchParams(window.location.search).get("otac");
|
||||
if (otac != null)
|
||||
useOtac(otac);
|
||||
useOtac(otac);
|
||||
|
@ -41,6 +41,15 @@ namespace Kyoo.Controllers
|
||||
.Take(20)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task<User> Create(User obj)
|
||||
{
|
||||
await base.Create(obj);
|
||||
_database.Entry(obj).State = EntityState.Added;
|
||||
await _database.SaveChangesAsync($"Trying to insert a duplicated user (slug {obj.Slug} already exists).");
|
||||
return obj;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task Delete(User obj)
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../Kyoo.Authentication/Kyoo.Authentication.csproj">
|
||||
<ExcludeAssets>all</ExcludeAssets>
|
||||
<!-- <ExcludeAssets>all</ExcludeAssets>-->
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Kyoo.Authentication;
|
||||
using Kyoo.Controllers;
|
||||
using Kyoo.Models;
|
||||
using Kyoo.Postgresql;
|
||||
@ -46,7 +47,7 @@ namespace Kyoo
|
||||
_configuration = configuration;
|
||||
_plugins = new PluginManager(hostProvider, _configuration, loggerFactory.CreateLogger<PluginManager>());
|
||||
|
||||
_plugins.LoadPlugins(new IPlugin[] {new CoreModule(), new PostgresModule(configuration, host)});
|
||||
_plugins.LoadPlugins(new IPlugin[] {new CoreModule(), new PostgresModule(configuration, host), new AuthenticationModule(configuration, loggerFactory)});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -126,19 +127,20 @@ namespace Kyoo
|
||||
app.UseResponseCompression();
|
||||
|
||||
_plugins.ConfigureAspnet(app);
|
||||
//
|
||||
// app.UseSpa(spa =>
|
||||
// {
|
||||
// spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp");
|
||||
//
|
||||
// if (env.IsDevelopment())
|
||||
// spa.UseAngularCliServer("start");
|
||||
// });
|
||||
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllerRoute("Kyoo", "api/{controller=Home}/{action=Index}/{id?}");
|
||||
});
|
||||
|
||||
|
||||
app.UseSpa(spa =>
|
||||
{
|
||||
spa.Options.SourcePath = Path.Join(AppDomain.CurrentDomain.BaseDirectory, "Kyoo.WebApp");
|
||||
|
||||
if (env.IsDevelopment())
|
||||
spa.UseAngularCliServer("start");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -20,8 +20,12 @@
|
||||
"default": "Trace",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information",
|
||||
"Microsoft.EntityFrameworkCore.DbUpdateException": "None",
|
||||
"Microsoft.EntityFrameworkCore.Update": "None",
|
||||
"Microsoft.EntityFrameworkCore.Database.Command": "None",
|
||||
"Kyoo": "Trace"
|
||||
}
|
||||
},
|
||||
"dotnet-ef": "false"
|
||||
},
|
||||
|
||||
"authentication": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user