Initial commit

This commit is contained in:
OlgunR 2024-09-06 10:59:27 +02:00
parent 7d01fe0e43
commit b811be2226
38 changed files with 1527 additions and 0 deletions

1
ProjectUserManager Submodule

@ -0,0 +1 @@
Subproject commit 7d01fe0e43a363d5c5edfe3279b970a589fd1e45

View File

@ -0,0 +1,86 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;
using UserManagement.Application.Dtos.Auth;
using UserManagement.Application.Interfaces;
using Swashbuckle.AspNetCore.Annotations;
namespace UserManagement.API.Controllers
{
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
// CTOR
private readonly IUserService _userService;
private readonly IAuthService _authService;
public AuthController(IUserService userService, IAuthService authService)
{
_userService = userService;
_authService = authService;
}
// LOGIN
[AllowAnonymous]
[HttpPost("login")]
[SwaggerOperation(Summary = "Login")]
public async Task<IActionResult> Login([FromBody] LoginDto login)
{
// Validate user
var user = await _userService.GetByUsernameAsync(login.Username);
if (user == null)
{
return Unauthorized();
}
// Validate login credentials
var isValid = await _authService.ValidateAsync(login.Username, login.Password);
if (!isValid)
{
return Unauthorized();
}
// Create claims based on the user information
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.UserName),
new Claim(ClaimTypes.Surname, user.LastName ?? ""),
new Claim(ClaimTypes.GivenName, user.FirstName ?? ""),
new Claim(ClaimTypes.Role, user?.Role?.Name.ToString() ?? "")
};
// Create a ClaimsIdentity based on the created claims
var claimsIdentity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
// Set the authentication properties
var authProperties = new AuthenticationProperties
{
IsPersistent = true,
AllowRefresh = true,
ExpiresUtc = DateTime.UtcNow.AddMinutes(60)
};
// Sign in user using cookie-based authentication
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties
);
return Ok();
}
// LOGOUT
[HttpPost("logout")]
[SwaggerOperation(Summary = "Logout")]
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
return Ok();
}
}
}

View File

@ -0,0 +1,133 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using UserManagement.Application.Dtos.Incomming;
using UserManagement.Application.Interfaces;
namespace UserManagement.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
//[Authorize(Roles = "Admin")]
public class RoleController : ControllerBase
{
// CTOR
private readonly IRoleService _roleService;
public RoleController(IRoleService roleService)
{
_roleService = roleService;
}
// CREATE
[HttpPost]
[SwaggerOperation(Summary = "Create Role")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> CreateRole([FromBody] CreatingRoleDto creatingRoleDto)
{
// Validate incomming model
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
// Try to add role asynchronously
var result = await _roleService.AddRoleAsync(creatingRoleDto);
// If role is successfully created, return a CreatedAtAction response with the created resource
if (result is not null)
{
var id = result.Id;
var createdResource = new { Id = id };
var actionName = nameof(GetRoleById);
var routeValue = new { id = createdResource.Id };
return CreatedAtAction(actionName, routeValue, createdResource);
}
else
{
return BadRequest("geht nix");
}
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
}
}
// READ ALL
[HttpGet]
[SwaggerOperation(Summary = "Get all Roles")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> GetRoles()
{
var roles = await _roleService.GetAllAsync();
return Ok(roles);
}
// READ BY ID
[HttpGet("id/{id}", Name = "GetRoleById")]
[SwaggerOperation(Summary = "Get Role by Id")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetRoleById(int id)
{
if (id <= 0)
{
return BadRequest("Invalid Id");
}
var role = await _roleService.GetByIdAsync(id);
if (role == null)
{
return NotFound();
}
return Ok(role);
}
// READ BY NAME
[HttpGet("name/{name}", Name = "GetRoleByName")]
[SwaggerOperation(Summary = "Get Role by Name")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetRoleByName(string name)
{
if (string.IsNullOrEmpty(name))
{
return BadRequest("Name cannot be empty");
}
var role = await _roleService.GetByNameAsync(name);
if (role == null)
{
return NotFound();
}
return Ok(role);
}
// UPDATE
[HttpPut("id/{id}", Name = "UpdateRole")]
[SwaggerOperation(Summary = "Update Role")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdateRole(int id, UpdatingRoleDto updatingRoleDto)
{
var updated = await _roleService.UpdateRoleAsync(updatingRoleDto);
return Ok(updated);
}
// DELETE
[HttpDelete("id/{id}", Name = "DeleteRole")]
[SwaggerOperation(Summary = "Delete Role")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> DeleteRole([FromRoute] int id)
{
await _roleService.DeleteRoleAsync(id);
return Ok();
}
}
}

View File

@ -0,0 +1,133 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Swashbuckle.AspNetCore.Annotations;
using UserManagement.Application.Dtos.Incomming;
using UserManagement.Application.Interfaces;
namespace UserManagement.API.Controllers
{
[Route("api/[controller]")]
[ApiController]
//[Authorize(Roles = "Admin")]
public class UserController : Controller
{
// CTOR
private readonly IUserService _userService;
public UserController(IUserService userService)
{
_userService = userService;
}
// CREATE
[HttpPost]
[SwaggerOperation(Summary = "Create User")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> CreateUser([FromBody] CreatingUserDto creatingUserDto)
{
// Validate incomming model
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
try
{
// Try to add user asynchronously
var result = await _userService.AddUserAsync(creatingUserDto);
// If user is successfully created, return a CreatedAtAction response with the created resource
if (result is not null)
{
var id = result.Id;
var createdResource = new { Id = id };
var actionName = nameof(GetUserById);
var routeValue = new { id = createdResource.Id };
return CreatedAtAction(actionName, routeValue, createdResource);
}
else
{
return BadRequest("Creation failed");
}
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
}
}
// READ ALL
[HttpGet]
[SwaggerOperation(Summary = "Get all Users")]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<IActionResult> GetAllUsers()
{
var users = await _userService.GetUsersAsync();
return Ok(users);
}
// READ BY ID
[HttpGet("id/{id}", Name = "GetUserById")]
[SwaggerOperation(Summary = "Get User by Id")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUserById(int id)
{
if (id <= 0)
{
return BadRequest("Invalid Id");
}
var user = await _userService.GetByIdAsync(id);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
// READ BY USERNAME
[HttpGet("username/{username}", Name = "GetUserByUsername")]
[SwaggerOperation(Summary = "Get User by Username")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetUserByUsername(string username)
{
if (string.IsNullOrEmpty(username))
{
return BadRequest("Username connot be empty");
}
var user = await _userService.GetByUsernameAsync(username);
if (user == null)
{
return NotFound();
}
return Ok(user);
}
// UPDATE
[HttpPut("id/{id}", Name = "UpdateUser")]
[SwaggerOperation(Summary = "Update User")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdateUser(int id, UpdatingUserDto updatingUserDto)
{
var updated = await _userService.UpdateUserAsync(updatingUserDto);
return Ok(updated);
}
// DELETE
[HttpDelete("id/{id}", Name = "DeleteUser")]
[SwaggerOperation(Summary = "Delete User")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> DeleteUser([FromBody] int id)
{
await _userService.DeleteUserAsync(id);
return Ok();
}
}
}

View File

@ -0,0 +1,107 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using UserManagement.Infrastructure;
#nullable disable
namespace UserManagement.API.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240821105311_Initial")]
partial class Initial
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("UserManagement.Domain.Entities.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime")
.HasColumnName("CREATION_DATE");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ROLE");
b.HasKey("Id");
b.ToTable("Roles");
});
modelBuilder.Entity("UserManagement.Domain.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("FIRST_NAME");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("LAST_NAME");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("PASSWORD");
b.Property<int?>("ROLE")
.HasColumnType("int");
b.Property<int>("RoleId")
.HasColumnType("int")
.HasColumnName("ROLE_ID");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("USER_NAME");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("Users");
});
modelBuilder.Entity("UserManagement.Domain.Entities.User", b =>
{
b.HasOne("UserManagement.Domain.Entities.Role", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,68 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace UserManagement.API.Migrations
{
/// <inheritdoc />
public partial class Initial : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "Roles",
columns: table => new
{
ID = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
ROLE = table.Column<string>(type: "nvarchar(max)", nullable: false),
CREATION_DATE = table.Column<DateTime>(type: "datetime", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_Roles", x => x.ID);
});
migrationBuilder.CreateTable(
name: "Users",
columns: table => new
{
ID = table.Column<int>(type: "int", nullable: false)
.Annotation("SqlServer:Identity", "1, 1"),
USER_NAME = table.Column<string>(type: "nvarchar(max)", nullable: false),
FIRST_NAME = table.Column<string>(type: "nvarchar(max)", nullable: false),
LAST_NAME = table.Column<string>(type: "nvarchar(max)", nullable: false),
PASSWORD = table.Column<string>(type: "nvarchar(max)", nullable: false),
ROLE_ID = table.Column<int>(type: "int", nullable: false),
ROLE = table.Column<int>(type: "int", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_Users", x => x.ID);
table.ForeignKey(
name: "FK_Users_Roles_ROLE_ID",
column: x => x.ROLE_ID,
principalTable: "Roles",
principalColumn: "ID",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_Users_ROLE_ID",
table: "Users",
column: "ROLE_ID");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "Users");
migrationBuilder.DropTable(
name: "Roles");
}
}
}

View File

@ -0,0 +1,104 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using UserManagement.Infrastructure;
#nullable disable
namespace UserManagement.API.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "8.0.8")
.HasAnnotation("Relational:MaxIdentifierLength", 128);
SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder);
modelBuilder.Entity("UserManagement.Domain.Entities.Role", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<DateTime>("CreationDate")
.HasColumnType("datetime")
.HasColumnName("CREATION_DATE");
b.Property<string>("Name")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("ROLE");
b.HasKey("Id");
b.ToTable("Roles");
});
modelBuilder.Entity("UserManagement.Domain.Entities.User", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("int")
.HasColumnName("ID");
SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property<int>("Id"));
b.Property<string>("FirstName")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("FIRST_NAME");
b.Property<string>("LastName")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("LAST_NAME");
b.Property<string>("Password")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("PASSWORD");
b.Property<int?>("ROLE")
.HasColumnType("int");
b.Property<int>("RoleId")
.HasColumnType("int")
.HasColumnName("ROLE_ID");
b.Property<string>("UserName")
.IsRequired()
.HasColumnType("nvarchar(max)")
.HasColumnName("USER_NAME");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("Users");
});
modelBuilder.Entity("UserManagement.Domain.Entities.User", b =>
{
b.HasOne("UserManagement.Domain.Entities.Role", "Role")
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,69 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.EntityFrameworkCore;
using UserManagement.Application.Interfaces;
using UserManagement.Application.MappingProfiles;
using UserManagement.Application.Services;
using UserManagement.Infrastructure;
using UserManagement.Infrastructure.Interfaces;
using UserManagement.Infrastructure.Repositories;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(s => s.EnableAnnotations());
builder.Services.AddAutoMapper(typeof(BasicDtoMappingProfile).Assembly);
builder.Services.AddScoped<IUserService, UserService>();
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IRoleService, RoleService>();
builder.Services.AddScoped<IRoleRepository, RoleRepository>();
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), b => b.MigrationsAssembly("UserManagement.API"));
});
builder.Services.AddMemoryCache();
builder.Services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
options.Cookie.SameSite = SameSiteMode.Strict;
options.LoginPath = "/api/auth/login";
options.LogoutPath = "/api/auth/logout";
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("AdminOnly", policy =>
policy.RequireRole("Admin"));
});
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();

View File

@ -0,0 +1,41 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:7080",
"sslPort": 44338
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "http://localhost:5084",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": true,
"launchUrl": "swagger",
"applicationUrl": "https://localhost:7288;http://localhost:5084",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UserManagement.App\UserManagement.Application.csproj" />
<ProjectReference Include="..\UserManagement.Domain\UserManagement.Domain.csproj" />
<ProjectReference Include="..\UserManagement.Infrastructure\UserManagement.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
@UserManagement.API_HostAddress = http://localhost:5084
GET {{UserManagement.API_HostAddress}}/weatherforecast/
Accept: application/json
###

View File

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View File

@ -0,0 +1,12 @@
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=UserManagement;Trusted_Connection=True;TrustServerCertificate=True;"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
}

View File

@ -0,0 +1,4 @@
namespace UserManagement.Application.Dtos.Auth
{
public record LoginDto(string Username, string Password);
}

View File

@ -0,0 +1,7 @@
namespace UserManagement.Application.Dtos.Incomming
{
public class CreatingRoleDto
{
public string Name { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace UserManagement.Application.Dtos.Incomming
{
public class CreatingUserDto
{
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; init; }
public int RoleId { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace UserManagement.Application.Dtos.Incomming
{
public class UpdatingRoleDto
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,17 @@
namespace UserManagement.Application.Dtos.Incomming
{
public class UpdatingUserDto
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Password { get; init; }
public int RoleId { get; set; }
}
}

View File

@ -0,0 +1,9 @@
namespace UserManagement.Application.Dtos.Outgoing
{
public class ReadingRoleDto
{
public int Id { get; set; }
public string Name { get; set; }
}
}

View File

@ -0,0 +1,15 @@
namespace UserManagement.Application.Dtos.Outgoing
{
public class ReadingUserDto
{
public int Id { get; set; }
public string UserName { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public ReadingRoleDto? Role { get; set; }
}
}

View File

@ -0,0 +1,8 @@
namespace UserManagement.Application.Interfaces
{
public interface IAuthService
{
// AUTHENTICATE
Task<bool> ValidateAsync(string username, string password);
}
}

View File

@ -0,0 +1,27 @@
using UserManagement.Application.Dtos.Incomming;
using UserManagement.Application.Dtos.Outgoing;
using UserManagement.Domain.Entities;
namespace UserManagement.Application.Interfaces
{
public interface IRoleService
{
// CREATE
Task<Role?> AddRoleAsync(CreatingRoleDto creatingRoleDto);
// READ ALL
Task<IEnumerable<ReadingRoleDto>> GetAllAsync();
// READ BY ID
Task<ReadingRoleDto> GetByIdAsync(int id);
// READ BY NAME
Task<ReadingRoleDto> GetByNameAsync(string name);
// UPDATE
Task<bool> UpdateRoleAsync(UpdatingRoleDto updatingRoleDto);
// DELETE
Task<bool> DeleteRoleAsync(int id);
}
}

View File

@ -0,0 +1,30 @@
using UserManagement.Application.Dtos.Incomming;
using UserManagement.Application.Dtos.Outgoing;
using UserManagement.Domain.Entities;
namespace UserManagement.Application.Interfaces
{
public interface IUserService
{
// CREATE
Task<User?> AddUserAsync(CreatingUserDto creatingUserDto);
// READ ALL
Task<IEnumerable<ReadingUserDto>> GetUsersAsync();
// READ BY ID
Task<ReadingUserDto> GetByIdAsync(int id);
// READ BY USERNAME
Task<ReadingUserDto> GetByUsernameAsync(string username);
// UPDATE
Task<bool> UpdateUserAsync(UpdatingUserDto updatingUserDto);
// UPDATE USER ROLE
Task UpdateUserRoleAsync(int userId, int roleId);
// DELETE
Task<bool> DeleteUserAsync(int id);
}
}

View File

@ -0,0 +1,23 @@
using AutoMapper;
using UserManagement.Application.Dtos.Incomming;
using UserManagement.Application.Dtos.Outgoing;
using UserManagement.Domain.Entities;
namespace UserManagement.Application.MappingProfiles
{
public class BasicDtoMappingProfile : Profile
{
public BasicDtoMappingProfile()
{
// ROLE
CreateMap<Role, CreatingRoleDto>().ReverseMap();
CreateMap<Role, ReadingRoleDto>().ReverseMap();
CreateMap<Role, UpdatingRoleDto>().ReverseMap();
// USER
CreateMap<User, CreatingUserDto>().ReverseMap();
CreateMap<User, ReadingUserDto>().ReverseMap();
CreateMap<User, UpdatingUserDto>().ReverseMap();
}
}
}

View File

@ -0,0 +1,23 @@
using UserManagement.Application.Interfaces;
using UserManagement.Infrastructure.Interfaces;
namespace UserManagement.Application.Services
{
public class AuthService : IAuthService
{
// CTOR
private IUserRepository _userRepository;
public AuthService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
// AUTHENTICATE
public async Task<bool> ValidateAsync(string username, string password)
{
var user = await _userRepository.GetByUsernameAsync(username);
return user?.Password == password;
}
}
}

View File

@ -0,0 +1,73 @@
using AutoMapper;
using UserManagement.Application.Dtos.Incomming;
using UserManagement.Application.Dtos.Outgoing;
using UserManagement.Application.Interfaces;
using UserManagement.Domain.Entities;
using UserManagement.Infrastructure.Interfaces;
namespace UserManagement.Application.Services
{
public class RoleService : IRoleService
{
// CTOR
private readonly IRoleRepository _roleRepository;
private readonly IMapper _mapper;
public RoleService(IRoleRepository roleRepository, IMapper mapper)
{
_roleRepository = roleRepository;
_mapper = mapper;
}
// CREATE
public async Task<Role?> AddRoleAsync(CreatingRoleDto creatingRoleDto)
{
var role = _mapper.Map<Role>(creatingRoleDto);
var created = await _roleRepository.AddAsync(role);
return created;
}
// READ ALL
public async Task<IEnumerable<ReadingRoleDto>> GetAllAsync()
{
var roles = await _roleRepository.GetAllAsync();
var readDto = _mapper.Map<IEnumerable<ReadingRoleDto>>(roles);
return readDto;
}
// READ BY ID
public async Task<ReadingRoleDto> GetByIdAsync(int id)
{
var role = await _roleRepository.GetByIdAsync(id);
var readDto = _mapper.Map<ReadingRoleDto>(role);
return readDto;
}
// READ BY NAME
public async Task<ReadingRoleDto> GetByNameAsync(string name)
{
var role = await _roleRepository.GetByNameAsync(name);
var readDto = _mapper.Map<ReadingRoleDto>(role);
return readDto;
}
// UPDATE
public async Task<bool> UpdateRoleAsync(UpdatingRoleDto updatingRoleDto)
{
var role = _mapper.Map<Role>(updatingRoleDto);
bool isUpdated = await _roleRepository.UpdateAsync(role);
return isUpdated;
}
// DELETE
public async Task<bool> DeleteRoleAsync(int id)
{
Role? role = await _roleRepository.GetByIdAsync(id);
if (role is null)
return false;
bool isDeleted = await _roleRepository.DeleteAsync(role);
return isDeleted;
}
}
}

View File

@ -0,0 +1,102 @@
using AutoMapper;
using UserManagement.Application.Dtos.Incomming;
using UserManagement.Application.Dtos.Outgoing;
using UserManagement.Application.Interfaces;
using UserManagement.Domain.Entities;
using UserManagement.Infrastructure.Interfaces;
namespace UserManagement.Application.Services
{
public class UserService : IUserService
{
// CTOR
private readonly IUserRepository _userRepository;
private readonly IRoleRepository _roleRepository;
private readonly IMapper _mapper;
public UserService(IUserRepository userRepository, IRoleRepository roleRepository, IMapper mapper)
{
_userRepository = userRepository;
_roleRepository = roleRepository;
_mapper = mapper;
}
// CREATE
public async Task<User?> AddUserAsync(CreatingUserDto creatingUserDto)
{
// validating role
var role = await _roleRepository.GetByIdAsync(creatingUserDto.RoleId);
if (role == null)
{
throw new ArgumentException("Role not found");
}
// mapping dto to entity
var user = _mapper.Map<User>(creatingUserDto);
var created = await _userRepository.AddAsync(user);
return created;
}
// READ ALL
public async Task<IEnumerable<ReadingUserDto>> GetUsersAsync()
{
var users = await _userRepository.GetAllAsync();
var readDto = _mapper.Map<IEnumerable<ReadingUserDto>>(users);
return readDto;
}
// READ BY ID
public async Task<ReadingUserDto> GetByIdAsync(int id)
{
var user = await _userRepository.GetByIdAsync(id);
var readDto = _mapper.Map<ReadingUserDto>(user);
return readDto;
}
// READ BY USERNAME
public async Task<ReadingUserDto> GetByUsernameAsync(string username)
{
var user = await _userRepository.GetByUsernameAsync(username);
var readDto = _mapper.Map<ReadingUserDto>(user);
return readDto;
}
// UPDATE
public async Task<bool> UpdateUserAsync(UpdatingUserDto updatingUserDto)
{
var user = _mapper.Map<User>(updatingUserDto);
bool isUpdated = await _userRepository.UpdateAsync(user);
return isUpdated;
}
// UPDATE USER ROLE -- die Rolle eines Users aktualisieren
public async Task UpdateUserRoleAsync(int userId, int roleId)
{
var user = await _userRepository.GetByIdAsync(userId);
if (user == null)
{
throw new ArgumentException("User not found");
}
var role = await _roleRepository.GetByIdAsync(roleId);
if (role == null)
{
throw new ArgumentException("Role not found");
}
user.RoleId = roleId;
await _userRepository.SaveAsync();
}
// DELETE
public async Task<bool> DeleteUserAsync(int id)
{
User? user = await _userRepository.GetByIdAsync(id);
if (user is null)
return false;
bool isDeleted = await _userRepository.DeleteAsync(user);
return isDeleted;
}
}
}

View File

@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UserManagement.Domain\UserManagement.Domain.csproj" />
<ProjectReference Include="..\UserManagement.Infrastructure\UserManagement.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,21 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace UserManagement.Domain.Entities
{
public class Role
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
[Column("ID")]
public int Id { get; set; }
[Required]
[Column("ROLE")]
public string Name { get; set; }
[Required]
[Column("CREATION_DATE", TypeName = "datetime")]
public DateTime CreationDate { get; set; } = DateTime.Now;
}
}

View File

@ -0,0 +1,35 @@
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace UserManagement.Domain.Entities
{
public class User
{
[Column("ID")]
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required]
[Column("USER_NAME")]
public string UserName { get; set; }
[Required]
[Column("FIRST_NAME")]
public string FirstName { get; set; }
[Required]
[Column("LAST_NAME")]
public string LastName { get; set; }
[Required]
[Column("PASSWORD")]
public string Password { get; init; }
[Column("ROLE_ID")]
public int RoleId { get; set; }
[ForeignKey("ROLE")]
public Role? Role { get; set; }
}
}

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.1" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,26 @@
using Microsoft.EntityFrameworkCore;
using UserManagement.Domain.Entities;
namespace UserManagement.Infrastructure
{
public class ApplicationDbContext : DbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
{
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>()
.HasOne(u => u.Role)
.WithMany()
.HasForeignKey(u => u.RoleId);
}
}
}

View File

@ -0,0 +1,25 @@
using UserManagement.Domain.Entities;
namespace UserManagement.Infrastructure.Interfaces
{
public interface IRoleRepository
{
// CREATE
Task<Role?> AddAsync(Role role);
// READ ALL
Task<IEnumerable<Role>> GetAllAsync();
// READ BY ID
Task<Role?> GetByIdAsync(int id);
// READ BY NAME
Task<Role?> GetByNameAsync(string name);
// UPDATE
Task<bool> UpdateAsync(Role role);
// DELETE
Task<bool> DeleteAsync(Role role);
}
}

View File

@ -0,0 +1,28 @@
using UserManagement.Domain.Entities;
namespace UserManagement.Infrastructure.Interfaces
{
public interface IUserRepository
{
// CREATE
Task<User?> AddAsync(User user);
// READ ALL
Task<IEnumerable<User>> GetAllAsync();
// READ BY ID
Task<User?> GetByIdAsync(int id);
// READ BY USERNAME
Task<User?> GetByUsernameAsync(string username);
// UPDATE
Task<bool> UpdateAsync(User user);
// DELETE
Task<bool> DeleteAsync(User user);
// SAVE
Task<bool> SaveAsync();
}
}

View File

@ -0,0 +1,58 @@
using Microsoft.EntityFrameworkCore;
using UserManagement.Domain.Entities;
using UserManagement.Infrastructure.Interfaces;
namespace UserManagement.Infrastructure.Repositories
{
public class RoleRepository : IRoleRepository
{
// CTOR
private readonly ApplicationDbContext _context;
public RoleRepository(ApplicationDbContext context)
{
_context = context;
}
// CREATE
public async Task<Role?> AddAsync(Role role)
{
await _context.Roles.AddAsync(role);
await _context.SaveChangesAsync();
return role;
}
// READ ALL
public async Task<IEnumerable<Role>> GetAllAsync()
{
return await _context.Roles.ToListAsync();
}
// READ BY ID
public async Task<Role?> GetByIdAsync(int id)
{
return await _context.Roles.FindAsync(id);
}
// READ BY NAME
public async Task<Role?> GetByNameAsync(string name)
{
return await _context.Roles.FirstOrDefaultAsync(n => n.Name == name);
}
// UPDATE
public async Task<bool> UpdateAsync(Role role)
{
_context.Entry(role).State = EntityState.Modified;
var results = await _context.SaveChangesAsync();
return results > 0;
}
// DELETE
public async Task<bool> DeleteAsync(Role role)
{
_context.Roles.Remove(role);
var result = await _context.SaveChangesAsync();
return result > 0;
}
}
}

View File

@ -0,0 +1,65 @@
using Microsoft.EntityFrameworkCore;
using UserManagement.Domain.Entities;
using UserManagement.Infrastructure.Interfaces;
namespace UserManagement.Infrastructure.Repositories
{
public class UserRepository : IUserRepository
{
// CTOR
private readonly ApplicationDbContext _context;
public UserRepository(ApplicationDbContext context)
{
_context = context;
}
// CREATE
public async Task<User?> AddAsync(User user)
{
await _context.Users.AddAsync(user);
await _context.SaveChangesAsync();
return user;
}
// READ ALL
public async Task<IEnumerable<User>> GetAllAsync()
{
return await _context.Users.Include(u => u.Role).ToListAsync();
}
// READ BY ID
public async Task<User?> GetByIdAsync(int id)
{
return await _context.Users.Where(user => user.Id == id).Include(user => user.Role).FirstAsync();
}
// READ BY USERNAME
public async Task<User?> GetByUsernameAsync(string username)
{
return await _context.Users.Include(user => user.Role).FirstOrDefaultAsync(u => u.UserName == username);
}
// UPDATE
public async Task<bool> UpdateAsync(User user)
{
_context.Entry(user).State = EntityState.Modified;
var results = await _context.SaveChangesAsync();
return results > 0;
}
// DELETE
public async Task<bool> DeleteAsync(User user)
{
_context.Users.Remove(user);
var result = await _context.SaveChangesAsync();
return result > 0;
}
// SAVE
public async Task<bool> SaveAsync()
{
var saved = await _context.SaveChangesAsync();
return saved > 0 ? true : false;
}
}
}

View File

@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.8">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Swashbuckle.AspNetCore.Annotations" Version="6.7.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\UserManagement.Domain\UserManagement.Domain.csproj" />
</ItemGroup>
</Project>

43
UserManagement.sln Normal file
View File

@ -0,0 +1,43 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.10.34916.146
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement.API", "UserManagement.API\UserManagement.API.csproj", "{549C7966-8315-4049-8E6E-5D5B81E846FC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement.Infrastructure", "UserManagement.Infrastructure\UserManagement.Infrastructure.csproj", "{AC25118D-000D-4063-A67B-452D5F6E7CBE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement.Domain", "UserManagement.Domain\UserManagement.Domain.csproj", "{B29D3BAF-0B74-4BC9-B720-8D1C0B8B7624}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UserManagement.Application", "UserManagement.App\UserManagement.Application.csproj", "{B2DF78C5-323C-4CCE-90D9-1C6B72678475}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{549C7966-8315-4049-8E6E-5D5B81E846FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{549C7966-8315-4049-8E6E-5D5B81E846FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{549C7966-8315-4049-8E6E-5D5B81E846FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{549C7966-8315-4049-8E6E-5D5B81E846FC}.Release|Any CPU.Build.0 = Release|Any CPU
{AC25118D-000D-4063-A67B-452D5F6E7CBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC25118D-000D-4063-A67B-452D5F6E7CBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC25118D-000D-4063-A67B-452D5F6E7CBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC25118D-000D-4063-A67B-452D5F6E7CBE}.Release|Any CPU.Build.0 = Release|Any CPU
{B29D3BAF-0B74-4BC9-B720-8D1C0B8B7624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B29D3BAF-0B74-4BC9-B720-8D1C0B8B7624}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B29D3BAF-0B74-4BC9-B720-8D1C0B8B7624}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B29D3BAF-0B74-4BC9-B720-8D1C0B8B7624}.Release|Any CPU.Build.0 = Release|Any CPU
{B2DF78C5-323C-4CCE-90D9-1C6B72678475}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2DF78C5-323C-4CCE-90D9-1C6B72678475}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2DF78C5-323C-4CCE-90D9-1C6B72678475}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2DF78C5-323C-4CCE-90D9-1C6B72678475}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9E5A8215-EA5D-45EF-BCA6-2F42899F383F}
EndGlobalSection
EndGlobal