mirror of
https://github.com/hmaxnl/SharpRSS.git
synced 2025-01-18 12:54:20 +01:00
Working on users database & api dto's
This commit is contained in:
parent
62244750de
commit
952e0d7e3e
21
SharpRSS.API.Contracts/ApiResult.cs
Normal file
21
SharpRSS.API.Contracts/ApiResult.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
namespace SharpRSS.API.Contracts
|
||||
{
|
||||
public class ApiResult
|
||||
{
|
||||
public ApiResult(string message, ApiResults result)
|
||||
{
|
||||
Message = message;
|
||||
Result = result;
|
||||
}
|
||||
public string Message { get; }
|
||||
public ApiResults Result { get; }
|
||||
}
|
||||
|
||||
public enum ApiResults
|
||||
{
|
||||
Ok,
|
||||
Warning,
|
||||
Error,
|
||||
Invalid
|
||||
}
|
||||
}
|
22
SharpRSS.API.Contracts/DTO/UserDto.cs
Normal file
22
SharpRSS.API.Contracts/DTO/UserDto.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
|
||||
namespace SharpRSS.API.Contracts.DTO
|
||||
{
|
||||
public class UserDto
|
||||
{
|
||||
public UserDto(string id, string userName, string mail, string role, DateTime dateCreated)
|
||||
{
|
||||
Id = id;
|
||||
UserName = userName;
|
||||
Mail = mail;
|
||||
Role = role;
|
||||
DateCreated = dateCreated;
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string UserName { get; }
|
||||
public string Mail { get; }
|
||||
public string Role { get; }
|
||||
public DateTime DateCreated { get; }
|
||||
}
|
||||
}
|
|
@ -13,7 +13,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<Folder Include="Auth" />
|
||||
<Folder Include="DTO" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using SharpRSS.API.Contracts;
|
||||
using SharpRSS.API.Contracts.DTO;
|
||||
using SharpRSS.API.Data;
|
||||
using SharpRSS.API.Models;
|
||||
using SharpRSS.API.Models.Auth;
|
||||
|
||||
namespace SharpRSS.API.Controllers
|
||||
{
|
||||
|
@ -7,10 +12,20 @@ namespace SharpRSS.API.Controllers
|
|||
[Route("api/[controller]")]
|
||||
public class AuthController : ControllerBase
|
||||
{
|
||||
[HttpGet("authenticate")]
|
||||
public async Task<ActionResult<string>> Authenticate()
|
||||
public AuthController(AuthService authService)
|
||||
{
|
||||
return "Authenticated!";
|
||||
_authService = authService;
|
||||
}
|
||||
|
||||
private readonly AuthService _authService;
|
||||
|
||||
[HttpPost("create")]
|
||||
public async Task<ActionResult<UserDto>> CreateUser(UserRequest user)
|
||||
{
|
||||
Result<User> result = await _authService.CreateUser(user);
|
||||
if (result.Success)
|
||||
return Ok(Models.Auth.User.ToDto(result.Value ?? new User()));
|
||||
return BadRequest(new ApiResult(result.Message, ApiResults.Error));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,16 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace SharpRSS.API.Controllers
|
||||
{
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class RssController
|
||||
public class RssController : ControllerBase
|
||||
{
|
||||
|
||||
[HttpGet("guide")]
|
||||
public async Task<ActionResult<string>> GetGuide()
|
||||
{
|
||||
return Ok("Guide data");
|
||||
}
|
||||
}
|
||||
}
|
34
SharpRSS.API/Cryptography/Hasher.cs
Normal file
34
SharpRSS.API/Cryptography/Hasher.cs
Normal file
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace SharpRSS.API.Cryptography
|
||||
{
|
||||
public static class Hasher
|
||||
{
|
||||
private const int KeySize = 128;
|
||||
private const int Iterations = 420069;
|
||||
static readonly HashAlgorithmName Algorithm = HashAlgorithmName.SHA512;
|
||||
|
||||
public static byte[] HashPassword(string password, out byte[] salt)
|
||||
{
|
||||
salt = RandomNumberGenerator.GetBytes(KeySize);
|
||||
return HashInternal(password, salt);
|
||||
}
|
||||
|
||||
public static bool ComparePasswords(string password, byte[] hash, byte[] salt)
|
||||
{
|
||||
byte[] passwordHashed = HashInternal(password, salt);
|
||||
if (hash.Length != passwordHashed.Length)
|
||||
return false;
|
||||
return !hash.Where((t, i) => t != passwordHashed[i]).Any();
|
||||
}
|
||||
|
||||
private static byte[] HashInternal(string password, byte[] salt)
|
||||
{
|
||||
var hash = Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), salt, Iterations, Algorithm,KeySize);
|
||||
return hash;
|
||||
}
|
||||
}
|
||||
}
|
56
SharpRSS.API/Data/AuthService.cs
Normal file
56
SharpRSS.API/Data/AuthService.cs
Normal file
|
@ -0,0 +1,56 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using SharpRSS.API.Cryptography;
|
||||
using SharpRSS.API.Models;
|
||||
using SharpRSS.API.Models.Auth;
|
||||
using ToolQit;
|
||||
using ToolQit.Logging;
|
||||
|
||||
namespace SharpRSS.API.Data
|
||||
{
|
||||
public class AuthService
|
||||
{
|
||||
public AuthService(IConfiguration configuration)
|
||||
{
|
||||
_configuration = configuration;
|
||||
_log = LogManager.CreateLogger(typeof(AuthService));
|
||||
_log.Information("Setting up service...");
|
||||
}
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILog _log;
|
||||
|
||||
public async Task<Result<User>> CreateUser(UserRequest userRequest)
|
||||
{
|
||||
bool result = false;
|
||||
await using DbAccess access = new DbAccess(_configuration);
|
||||
|
||||
var user = access.Users.FirstOrDefault(u => u.UserName == userRequest.UserName);
|
||||
if (user != null)
|
||||
return new Result<User>(user, message:"User name already exists!");
|
||||
byte[] hashedPwdBytes = Hasher.HashPassword(userRequest.Password, out byte[] salt);
|
||||
user = new User()
|
||||
{
|
||||
UserName = userRequest.UserName,
|
||||
Mail = userRequest.EMail,
|
||||
Password = hashedPwdBytes,
|
||||
Salt = salt
|
||||
};
|
||||
access.Users.Add(user);
|
||||
try
|
||||
{
|
||||
int entries = await access.SaveChangesAsync();
|
||||
result = entries > 0;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.Error(e, "Error creating user: {UserName}", user.UserName);
|
||||
return new Result<User>(user, message: "Could not create user!");
|
||||
}
|
||||
return new Result<User>(user, result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace SharpRSS.API.Data
|
||||
{
|
||||
public class AuthenticationService
|
||||
{
|
||||
|
||||
}
|
||||
}
|
48
SharpRSS.API/Data/DbAccess.cs
Normal file
48
SharpRSS.API/Data/DbAccess.cs
Normal file
|
@ -0,0 +1,48 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using SharpRSS.API.Models.Auth;
|
||||
using ToolQit;
|
||||
using ToolQit.Logging;
|
||||
|
||||
namespace SharpRSS.API.Data
|
||||
{
|
||||
public sealed class DbAccess : DbContext
|
||||
{
|
||||
public DbAccess(IConfiguration configuration)
|
||||
{
|
||||
_log = LogManager.CreateLogger(typeof(DbAccess));
|
||||
_configuration = configuration;
|
||||
Database.EnsureCreated();
|
||||
}
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
|
||||
private readonly IConfiguration _configuration;
|
||||
private readonly ILog _log;
|
||||
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||
{
|
||||
string connection = _configuration["DataBase:Connection"] ?? throw new ArgumentNullException(nameof(_configuration), "No connection string in appsettings!");
|
||||
string server = _configuration["DataBase:Server"] ?? "Unknown";
|
||||
switch (server)
|
||||
{
|
||||
case "MariaDB":
|
||||
var dbSrvVersion = ServerVersion.AutoDetect(connection);
|
||||
_log.Information("Server {SrvType} detected, version: {SrvVersion}", dbSrvVersion.Type.ToString(), dbSrvVersion.Version);
|
||||
optionsBuilder.UseMySql(connection, dbSrvVersion);
|
||||
break;
|
||||
default: // TODO: Add more database support.
|
||||
_log.Warning("No valid db server: {Server}/nSupported db servers: 'MariaDB'", server);
|
||||
throw new Exception("Database server not specified!");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<User>().ToTable("srss_user");
|
||||
base.OnModelCreating(modelBuilder);
|
||||
}
|
||||
}
|
||||
}
|
27
SharpRSS.API/Models/Auth/User.cs
Normal file
27
SharpRSS.API/Models/Auth/User.cs
Normal file
|
@ -0,0 +1,27 @@
|
|||
using System;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using SharpRSS.API.Contracts.DTO;
|
||||
|
||||
namespace SharpRSS.API.Models.Auth
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public static UserDto ToDto(User user) => new UserDto(user.Id, user.UserName, user.Mail, user.Role, user.DateCreated);
|
||||
|
||||
[Key]
|
||||
public string Id { get; set; } = Guid.NewGuid().ToString();
|
||||
[Required]
|
||||
public string UserName { get; set; }
|
||||
[Required]
|
||||
[EmailAddress]
|
||||
public string Mail { get; set; }
|
||||
[Required]
|
||||
public byte[] Password { get; set; }
|
||||
[Required]
|
||||
public byte[] Salt { get; set; }
|
||||
[Required]
|
||||
public string Role { get; set; } = "User";
|
||||
[Required]
|
||||
public DateTime DateCreated { get; set; } = DateTime.Now;
|
||||
}
|
||||
}
|
9
SharpRSS.API/Models/Auth/UserRequest.cs
Normal file
9
SharpRSS.API/Models/Auth/UserRequest.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
namespace SharpRSS.API.Models.Auth
|
||||
{
|
||||
public class UserRequest
|
||||
{
|
||||
public string UserName { get; set; }
|
||||
public string EMail { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
15
SharpRSS.API/Models/Result.cs
Normal file
15
SharpRSS.API/Models/Result.cs
Normal file
|
@ -0,0 +1,15 @@
|
|||
namespace SharpRSS.API.Models
|
||||
{
|
||||
public class Result<TValue>
|
||||
{
|
||||
public Result(TValue value, bool success = false, string message = "")
|
||||
{
|
||||
Value = value;
|
||||
Success = success;
|
||||
Message = message;
|
||||
}
|
||||
public TValue? Value { get; }
|
||||
public bool Success { get; }
|
||||
public string Message { get; }
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ using SharpRSS.API.Data;
|
|||
using ToolQit;
|
||||
using ToolQit.Logging.Serilog;
|
||||
|
||||
|
||||
SetupSerilog();
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
builder.Logging.AddSerilog();
|
||||
|
@ -18,7 +19,7 @@ builder.Services.AddControllers();
|
|||
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen();
|
||||
builder.Services.AddScoped<AuthenticationService>();
|
||||
builder.Services.AddScoped<AuthService>();
|
||||
builder.Services.AddScoped<SharpRssService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="7.0.9" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.10" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.1.0" />
|
||||
|
@ -22,8 +24,4 @@
|
|||
<ProjectReference Include="..\ToolQit\ToolQit\ToolQit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Models" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
{
|
||||
"DataBase": {
|
||||
"Server": "MariaDB",
|
||||
"Connection": "server=192.168.1.4;user id=api_dev;password=B4OqGO50qCxZXESD;database=sharprss_dev"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
|
|
19
SharpRSS.Blazor/Data/SrssAuthenticationStateProvider.cs
Normal file
19
SharpRSS.Blazor/Data/SrssAuthenticationStateProvider.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.Security.Claims;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
|
||||
namespace SharpRSS.Blazor.Data
|
||||
{
|
||||
public class SrssAuthenticationStateProvider : AuthenticationStateProvider
|
||||
{
|
||||
public override Task<AuthenticationState> GetAuthenticationStateAsync()
|
||||
{
|
||||
var identity = new ClaimsIdentity(new[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, "srss")
|
||||
}, "Custom Authentication");
|
||||
var user = new ClaimsPrincipal(identity);
|
||||
return Task.FromResult(new AuthenticationState(user));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,11 +1,13 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Components.Authorization;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using MudBlazor.Services;
|
||||
using Serilog;
|
||||
using Serilog.Formatting.Compact;
|
||||
using SharpRSS.Blazor.Data;
|
||||
using ToolQit;
|
||||
using ToolQit.Logging.Serilog;
|
||||
|
||||
|
@ -15,6 +17,7 @@ builder.Logging.AddSerilog();
|
|||
// Add services to the container.
|
||||
builder.Services.AddRazorPages();
|
||||
builder.Services.AddServerSideBlazor();
|
||||
builder.Services.AddScoped<AuthenticationStateProvider, SrssAuthenticationStateProvider>();
|
||||
builder.Services.AddMudServices();
|
||||
|
||||
var app = builder.Build();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="7.0.10" />
|
||||
<PackageReference Include="MudBlazor" Version="6.9.0" />
|
||||
<PackageReference Include="Serilog" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="7.0.0" />
|
||||
|
@ -21,8 +22,4 @@
|
|||
<ProjectReference Include="..\ToolQit\ToolQit\ToolQit.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Data" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
Loading…
Reference in New Issue
Block a user