commit 897851f80aec0fb1fa972ce78f3dee6665b381d8
Author: Vojtěch Bartička <aitakaitov@gmail.com>
Date:   Sun Apr 3 10:34:11 2022 +0200

    Added UserService impl., service registration

diff --git a/Backend/Backend/Controllers/WeatherForecastController.cs b/Backend/Backend/Controllers/WeatherForecastController.cs
index a1eee59..d748f28 100644
--- a/Backend/Backend/Controllers/WeatherForecastController.cs
+++ b/Backend/Backend/Controllers/WeatherForecastController.cs
@@ -2,6 +2,7 @@ using Core.Contexts;
 using Microsoft.AspNetCore.Mvc;
 using Core.Entities;
 using Core.Enums;
+using Core.Services;
 
 namespace Backend.Controllers
 {
@@ -10,7 +11,7 @@ namespace Backend.Controllers
     [Route("[controller]")]
     public class WeatherForecastController : ControllerBase
     {
-        private readonly DatabaseContext _databaseContext;
+        private readonly IUserService _userService;
 
         private static readonly string[] Summaries = new[]
         {
@@ -19,15 +20,20 @@ namespace Backend.Controllers
 
         private readonly ILogger<WeatherForecastController> _logger;
 
-        public WeatherForecastController(ILogger<WeatherForecastController> logger, DatabaseContext databaseContext)
+        public WeatherForecastController(ILogger<WeatherForecastController> logger, IUserService userService)
         {
             _logger = logger;
-            _databaseContext = databaseContext;
+            _userService = userService;
         }
 
         [HttpGet(Name = "GetWeatherForecast")]
         public IEnumerable<WeatherForecast> Get()
-        {     
+        {
+            /*User? u = _userService.CreateUser("username2", "name", "surname", "password", ERole.ANNOTATOR);
+            u = _userService.UpdateUser(u, "username3");
+            u = _userService.ChangePassword(u, "password2");*/
+            User u = _userService.CheckUsernamePassword("asdfghjkl", "password2");
+
             return Enumerable.Range(1, 5).Select(index => new WeatherForecast
             {
                 Date = DateTime.Now.AddDays(index),
diff --git a/Backend/Backend/Program.cs b/Backend/Backend/Program.cs
index d50cf82..6241bdd 100644
--- a/Backend/Backend/Program.cs
+++ b/Backend/Backend/Program.cs
@@ -1,5 +1,6 @@
 using Core.Contexts;
 using Core.Seeding;
+using Core.Services;
 using Serilog;
 
 
@@ -10,6 +11,9 @@ builder.Host.UseSerilog((ctx, lc) => lc.WriteTo.Console());
 
 builder.Services.AddControllers();
 
+// Register our services
+Registration.RegisterServices(builder);
+
 // Swagger
 builder.Services.AddEndpointsApiExplorer();
 builder.Services.AddSwaggerGen();
@@ -40,7 +44,6 @@ app.Logger.LogInformation("Starting up");
 // Database seeding
 using (var scope = app.Services.CreateScope())
 {
-    var services = scope.ServiceProvider;
     var context = scope.ServiceProvider.GetService<DatabaseContext>();
 
     // In development we seed dummy data
diff --git a/Backend/Core/Core.csproj b/Backend/Core/Core.csproj
index e84c590..4e9b197 100644
--- a/Backend/Core/Core.csproj
+++ b/Backend/Core/Core.csproj
@@ -7,6 +7,7 @@
   </PropertyGroup>
 
   <ItemGroup>
+    <PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
     <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.3" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.3" />
     <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="6.0.3">
@@ -14,10 +15,9 @@
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
     </PackageReference>
     <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="6.0.3" />
-  </ItemGroup>
-
-  <ItemGroup>
-    <Folder Include="Services\" />
+    <PackageReference Include="Serilog" Version="2.10.0" />
+    <PackageReference Include="Serilog.AspNetCore" Version="5.0.0" />
+    <PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
   </ItemGroup>
 
 </Project>
diff --git a/Backend/Core/Services/Registration.cs b/Backend/Core/Services/Registration.cs
new file mode 100644
index 0000000..30505f5
--- /dev/null
+++ b/Backend/Core/Services/Registration.cs
@@ -0,0 +1,18 @@
+﻿using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Core.Services
+{
+    public class Registration
+    {
+        public static void RegisterServices(WebApplicationBuilder builder)
+        {
+            builder.Services.AddScoped<IUserService, UserServiceEF>();
+        }
+    }
+}
diff --git a/Backend/Core/Services/UserService/IUserService.cs b/Backend/Core/Services/UserService/IUserService.cs
new file mode 100644
index 0000000..dfcbce0
--- /dev/null
+++ b/Backend/Core/Services/UserService/IUserService.cs
@@ -0,0 +1,20 @@
+﻿using Core.Entities;
+using Core.Enums;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Core.Services
+{
+    public interface IUserService
+    {
+        public User? CreateUser(string username, string name, string surname, string password, ERole role);
+        public User? GetUserByUsername(string username);
+        public User? GetUserByGuid(Guid id);
+        public User UpdateUser(User user, string? username = null, string? name = null, string? surname = null, ERole? role = null);
+        public User ChangePassword(User user, string newPassword);
+        public User? CheckUsernamePassword(string username, string password);
+    }
+}
diff --git a/Backend/Core/Services/UserService/UserServiceEF.cs b/Backend/Core/Services/UserService/UserServiceEF.cs
new file mode 100644
index 0000000..60ee80e
--- /dev/null
+++ b/Backend/Core/Services/UserService/UserServiceEF.cs
@@ -0,0 +1,138 @@
+﻿using Core.Contexts;
+using Core.Entities;
+using Core.Enums;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Serilog;
+using BCrypt.Net;
+
+namespace Core.Services
+{
+    public class UserServiceEF : IUserService
+    {
+        private readonly DatabaseContext _databaseContext;
+        private readonly ILogger _logger;
+
+        public UserServiceEF(DatabaseContext context, ILogger logger)
+        {
+            _databaseContext = context;
+            _logger = logger;
+        }
+
+        public User ChangePassword(User user, string newPassword)
+        {
+            // Check if the user is tracked by EF
+            if (!_databaseContext.Users.Local.Any(u => u.Id == user.Id))
+            {
+                _logger.Information($"User {user.Username} with Guid {user.Id} is untracked by EF and password change cannot be done.");
+                throw new InvalidOperationException("User is untracked by EF.");
+            }
+
+            user.Password = BCrypt.Net.BCrypt.HashPassword(newPassword);
+            _databaseContext.SaveChanges();
+            return user;
+        }
+
+        public User? CheckUsernamePassword(string username, string password)
+        {
+            try
+            {
+                // Throws exception if user does not exist
+                User u = _databaseContext.Users.First(u => u.Username == username);
+                if (!BCrypt.Net.BCrypt.Verify(password, u.Password))
+                {
+                    _logger.Information($"Password for user {username} don't match.");
+                    return null;
+                }
+                return u;
+            }
+            catch (Exception ex)
+            {
+                _logger.Information($"No user with username {username} found.");
+                return null;
+            }
+        }
+
+        public User? CreateUser(string username, string name, string surname, string password, ERole role)
+        {
+            // Check if username already used
+            if (_databaseContext.Users.Any(u => u.Username == username))
+            {
+                _logger.Information($"Username {username} is already used. Cannot create new user");
+                return null;
+            }
+
+            User user = new User() { Username = username, Name = name, Surname = surname, Password = BCrypt.Net.BCrypt.HashPassword(password), Role = role };
+            _databaseContext.Users.Add(user);
+            _databaseContext.SaveChanges();
+            return user;
+        }
+
+        public User? GetUserByUsername(string username)
+        {
+            try
+            {
+                // Throws exception on no user found
+                User user = _databaseContext.Users.First(u => u.Username == username);
+                return user;
+            }
+            catch (InvalidOperationException ex)
+            {
+                _logger.Warning($"No user with the username {username} found.");
+                return null;
+            }
+        }
+
+        public User? GetUserByGuid(Guid id)
+        {
+            try
+            {
+                // Throws exception on no user found
+                User user = _databaseContext.Users.First(u => u.Id == id);
+                return user;
+            }
+            catch (InvalidOperationException ex)
+            {
+                _logger.Warning($"No user with the GUID {id} found.");
+                return null;
+            }
+        }
+
+        public User UpdateUser(User user, string? username = null, string? name = null, string? surname = null, ERole? role = null)
+        {
+            // Check if the user is tracked by EF
+            if (!_databaseContext.Users.Local.Any(u => u.Id == user.Id))
+            {
+                _logger.Information($"User {user.Username} with Guid {user.Id} is untracked by EF and update cannot be done.");
+                throw new InvalidOperationException("User is untracked by EF.");
+            }
+
+            if (username is not null)
+            {
+                user.Username = username;
+            }
+
+            if (name is not null)
+            {
+                user.Name = name;
+            }
+
+            if (surname is not null)
+            {
+                user.Surname = surname;
+            }
+
+            if (role is not null)
+            {
+                // We know it is not null
+                user.Role = (ERole)role;
+            }
+
+            _databaseContext.SaveChanges();
+            return user;
+        }
+    }
+}
