diff --git a/SharpRSS.API.Contracts/ApiResult.cs b/SharpRSS.API.Contracts/ApiResult.cs
new file mode 100644
index 0000000..adf5af4
--- /dev/null
+++ b/SharpRSS.API.Contracts/ApiResult.cs
@@ -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
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API.Contracts/DTO/UserDto.cs b/SharpRSS.API.Contracts/DTO/UserDto.cs
new file mode 100644
index 0000000..bfb3483
--- /dev/null
+++ b/SharpRSS.API.Contracts/DTO/UserDto.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API.Contracts/SharpRSS.API.Contracts.csproj b/SharpRSS.API.Contracts/SharpRSS.API.Contracts.csproj
index ccefe79..5cf2042 100644
--- a/SharpRSS.API.Contracts/SharpRSS.API.Contracts.csproj
+++ b/SharpRSS.API.Contracts/SharpRSS.API.Contracts.csproj
@@ -13,7 +13,6 @@
-
diff --git a/SharpRSS.API/Controllers/AuthController.cs b/SharpRSS.API/Controllers/AuthController.cs
index d778a0b..82d4695 100644
--- a/SharpRSS.API/Controllers/AuthController.cs
+++ b/SharpRSS.API/Controllers/AuthController.cs
@@ -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> Authenticate()
+ public AuthController(AuthService authService)
{
- return "Authenticated!";
+ _authService = authService;
+ }
+
+ private readonly AuthService _authService;
+
+ [HttpPost("create")]
+ public async Task> CreateUser(UserRequest user)
+ {
+ Result 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));
}
}
}
\ No newline at end of file
diff --git a/SharpRSS.API/Controllers/RssController.cs b/SharpRSS.API/Controllers/RssController.cs
index 074e415..91da0df 100644
--- a/SharpRSS.API/Controllers/RssController.cs
+++ b/SharpRSS.API/Controllers/RssController.cs
@@ -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> GetGuide()
+ {
+ return Ok("Guide data");
+ }
}
}
\ No newline at end of file
diff --git a/SharpRSS.API/Cryptography/Hasher.cs b/SharpRSS.API/Cryptography/Hasher.cs
new file mode 100644
index 0000000..95831bc
--- /dev/null
+++ b/SharpRSS.API/Cryptography/Hasher.cs
@@ -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;
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API/Data/AuthService.cs b/SharpRSS.API/Data/AuthService.cs
new file mode 100644
index 0000000..20f0ae2
--- /dev/null
+++ b/SharpRSS.API/Data/AuthService.cs
@@ -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> 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, 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, message: "Could not create user!");
+ }
+ return new Result(user, result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API/Data/AuthenticationService.cs b/SharpRSS.API/Data/AuthenticationService.cs
deleted file mode 100644
index 5ffd196..0000000
--- a/SharpRSS.API/Data/AuthenticationService.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace SharpRSS.API.Data
-{
- public class AuthenticationService
- {
-
- }
-}
\ No newline at end of file
diff --git a/SharpRSS.API/Data/DbAccess.cs b/SharpRSS.API/Data/DbAccess.cs
new file mode 100644
index 0000000..281a7c9
--- /dev/null
+++ b/SharpRSS.API/Data/DbAccess.cs
@@ -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 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().ToTable("srss_user");
+ base.OnModelCreating(modelBuilder);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API/Models/Auth/User.cs b/SharpRSS.API/Models/Auth/User.cs
new file mode 100644
index 0000000..7e54e5f
--- /dev/null
+++ b/SharpRSS.API/Models/Auth/User.cs
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API/Models/Auth/UserRequest.cs b/SharpRSS.API/Models/Auth/UserRequest.cs
new file mode 100644
index 0000000..9dbdb09
--- /dev/null
+++ b/SharpRSS.API/Models/Auth/UserRequest.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API/Models/Result.cs b/SharpRSS.API/Models/Result.cs
new file mode 100644
index 0000000..7db58e3
--- /dev/null
+++ b/SharpRSS.API/Models/Result.cs
@@ -0,0 +1,15 @@
+namespace SharpRSS.API.Models
+{
+ public class Result
+ {
+ 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; }
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.API/Program.cs b/SharpRSS.API/Program.cs
index 56522f5..58c955f 100644
--- a/SharpRSS.API/Program.cs
+++ b/SharpRSS.API/Program.cs
@@ -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();
+builder.Services.AddScoped();
builder.Services.AddScoped();
var app = builder.Build();
diff --git a/SharpRSS.API/SharpRSS.API.csproj b/SharpRSS.API/SharpRSS.API.csproj
index f8bb911..77dbe9a 100644
--- a/SharpRSS.API/SharpRSS.API.csproj
+++ b/SharpRSS.API/SharpRSS.API.csproj
@@ -9,6 +9,8 @@
+
+
@@ -22,8 +24,4 @@
-
-
-
-
diff --git a/SharpRSS.API/appsettings.Development.json b/SharpRSS.API/appsettings.Development.json
index 0c208ae..9cd5ba8 100644
--- a/SharpRSS.API/appsettings.Development.json
+++ b/SharpRSS.API/appsettings.Development.json
@@ -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",
diff --git a/SharpRSS.Blazor/Data/SrssAuthenticationStateProvider.cs b/SharpRSS.Blazor/Data/SrssAuthenticationStateProvider.cs
new file mode 100644
index 0000000..8fa8383
--- /dev/null
+++ b/SharpRSS.Blazor/Data/SrssAuthenticationStateProvider.cs
@@ -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 GetAuthenticationStateAsync()
+ {
+ var identity = new ClaimsIdentity(new[]
+ {
+ new Claim(ClaimTypes.Name, "srss")
+ }, "Custom Authentication");
+ var user = new ClaimsPrincipal(identity);
+ return Task.FromResult(new AuthenticationState(user));
+ }
+ }
+}
\ No newline at end of file
diff --git a/SharpRSS.Blazor/Program.cs b/SharpRSS.Blazor/Program.cs
index 40fda78..9215c85 100644
--- a/SharpRSS.Blazor/Program.cs
+++ b/SharpRSS.Blazor/Program.cs
@@ -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();
builder.Services.AddMudServices();
var app = builder.Build();
diff --git a/SharpRSS.Blazor/SharpRSS.Blazor.csproj b/SharpRSS.Blazor/SharpRSS.Blazor.csproj
index 6b88525..9ab30e8 100644
--- a/SharpRSS.Blazor/SharpRSS.Blazor.csproj
+++ b/SharpRSS.Blazor/SharpRSS.Blazor.csproj
@@ -8,6 +8,7 @@
+
@@ -21,8 +22,4 @@
-
-
-
-