generated from egmont11/ASP.Net-Core-MVC-Template
InitEshop+ddd+ca
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
using Eshop.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Eshop.Infrastructure;
|
||||
|
||||
public class AppDbContext : IdentityDbContext<UserEntity>
|
||||
{
|
||||
public AppDbContext(DbContextOptions<AppDbContext> options)
|
||||
: base(options)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Eshop.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Eshop.Infrastructure.Data;
|
||||
|
||||
public static class DataSeeder
|
||||
{
|
||||
public static async Task SeedAsync(UserManager<UserEntity> userManager, RoleManager<IdentityRole> roleManager)
|
||||
{
|
||||
// Ensure roles exist
|
||||
foreach (var role in new[] { "Admin", "User" })
|
||||
{
|
||||
if (!await roleManager.RoleExistsAsync(role))
|
||||
await roleManager.CreateAsync(new IdentityRole(role));
|
||||
}
|
||||
|
||||
// Seed initial admin user if none exists
|
||||
if (!userManager.Users.Any())
|
||||
{
|
||||
var admin = new UserEntity
|
||||
{
|
||||
UserName = "admin",
|
||||
Email = "admin@template.com",
|
||||
EmailConfirmed = true,
|
||||
};
|
||||
|
||||
// You should change this password on first login
|
||||
var result = await userManager.CreateAsync(admin, "Admin123!");
|
||||
if (result.Succeeded)
|
||||
await userManager.AddToRoleAsync(admin, "Admin");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
using Eshop.Application.Auth;
|
||||
using Eshop.Infrastructure.Identity;
|
||||
using Eshop.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Eshop.Infrastructure;
|
||||
|
||||
public static class DependencyInjection
|
||||
{
|
||||
public static IServiceCollection AddInfrastructure(this IServiceCollection services) =>
|
||||
AddApplicationServices(services);
|
||||
|
||||
public static IServiceCollection AddInfrastructure(
|
||||
this IServiceCollection services,
|
||||
IConfiguration configuration)
|
||||
{
|
||||
var connectionString = configuration.GetConnectionString("DefaultConnection")
|
||||
?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
|
||||
|
||||
services.AddDbContext<AppDbContext>(options =>
|
||||
options.UseNpgsql(connectionString));
|
||||
|
||||
var pwSection = configuration.GetSection("Identity:Password");
|
||||
services.AddIdentity<UserEntity, IdentityRole>(options =>
|
||||
{
|
||||
options.Password.RequiredLength = pwSection.GetValue<int>("RequiredLength", 6);
|
||||
options.Password.RequireDigit = pwSection.GetValue<bool>("RequireDigit", true);
|
||||
options.Password.RequireLowercase = pwSection.GetValue<bool>("RequireLowercase", true);
|
||||
options.Password.RequireUppercase = pwSection.GetValue<bool>("RequireUppercase", true);
|
||||
options.Password.RequireNonAlphanumeric = pwSection.GetValue<bool>("RequireNonAlphanumeric", false);
|
||||
options.Password.RequiredUniqueChars = pwSection.GetValue<int>("RequiredUniqueChars", 1);
|
||||
})
|
||||
.AddEntityFrameworkStores<AppDbContext>()
|
||||
.AddDefaultTokenProviders();
|
||||
|
||||
services.ConfigureApplicationCookie(options =>
|
||||
{
|
||||
options.LoginPath = "/Auth/Login";
|
||||
options.AccessDeniedPath = "/Auth/AccessDenied";
|
||||
options.ExpireTimeSpan = TimeSpan.FromHours(2);
|
||||
});
|
||||
|
||||
return AddApplicationServices(services);
|
||||
}
|
||||
|
||||
private static IServiceCollection AddApplicationServices(IServiceCollection services)
|
||||
{
|
||||
services.AddScoped<IAuthenticationService, AspNetIdentityAuthenticationService>();
|
||||
services.AddScoped<IUserAccountService, AspNetIdentityUserAccountService>();
|
||||
services.AddScoped<IUserAdministrationService, AspNetIdentityUserAdministrationService>();
|
||||
return services;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<FrameworkReference Include="Microsoft.AspNetCore.App" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.10" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.10">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Eshop.Application\Eshop.Application.csproj" />
|
||||
<ProjectReference Include="..\Eshop.Domain\Eshop.Domain.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,30 @@
|
||||
using Eshop.Application.Auth;
|
||||
using Eshop.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Eshop.Infrastructure.Identity;
|
||||
|
||||
public class AspNetIdentityAuthenticationService(
|
||||
UserManager<UserEntity> users,
|
||||
SignInManager<UserEntity> signIn) : IAuthenticationService
|
||||
{
|
||||
public async Task<SignInResultDto> SignInAsync(
|
||||
string emailOrUsername,
|
||||
string password,
|
||||
bool rememberMe,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var user = emailOrUsername.Contains('@')
|
||||
? await users.FindByEmailAsync(emailOrUsername)
|
||||
: await users.FindByNameAsync(emailOrUsername);
|
||||
|
||||
if (user is null)
|
||||
return SignInResultDto.Failure;
|
||||
|
||||
var result = await signIn.PasswordSignInAsync(user, password, rememberMe, lockoutOnFailure: false);
|
||||
return result.Succeeded ? SignInResultDto.Success : SignInResultDto.Failure;
|
||||
}
|
||||
|
||||
public Task SignOutAsync(CancellationToken cancellationToken) =>
|
||||
signIn.SignOutAsync();
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
using Eshop.Application.Auth;
|
||||
using Eshop.Application.Common;
|
||||
using Eshop.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Eshop.Infrastructure.Identity;
|
||||
|
||||
public class AspNetIdentityUserAccountService(
|
||||
UserManager<UserEntity> users,
|
||||
SignInManager<UserEntity> signIn) : IUserAccountService
|
||||
{
|
||||
public async Task<ServiceResult> RegisterAsync(
|
||||
string userName,
|
||||
string email,
|
||||
string password,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var user = new UserEntity
|
||||
{
|
||||
UserName = userName.Trim(),
|
||||
Email = email.Trim().ToLower(),
|
||||
};
|
||||
|
||||
var result = await users.CreateAsync(user, password);
|
||||
if (!result.Succeeded)
|
||||
return ServiceResult.Failure(result.Errors.Select(e => e.Description));
|
||||
|
||||
await users.AddToRoleAsync(user, "User");
|
||||
return ServiceResult.Ok();
|
||||
}
|
||||
|
||||
public async Task<UserProfileDto?> GetProfileAsync(string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await users.FindByIdAsync(userId);
|
||||
if (user is null) return null;
|
||||
|
||||
return new UserProfileDto(user.UserName!, user.Email!);
|
||||
}
|
||||
|
||||
public async Task<ServiceResult> UpdateProfileAsync(
|
||||
string userId,
|
||||
UpdateProfileRequest request,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await users.FindByIdAsync(userId);
|
||||
if (user is null)
|
||||
return ServiceResult.Failure("User not found.");
|
||||
|
||||
var errors = new List<string>();
|
||||
|
||||
if (user.UserName != request.UserName.Trim())
|
||||
{
|
||||
var setName = await users.SetUserNameAsync(user, request.UserName.Trim());
|
||||
if (!setName.Succeeded)
|
||||
errors.AddRange(setName.Errors.Select(e => e.Description));
|
||||
}
|
||||
|
||||
var normalizedEmail = request.Email.Trim().ToLower();
|
||||
if (user.Email != normalizedEmail)
|
||||
{
|
||||
var setEmail = await users.SetEmailAsync(user, normalizedEmail);
|
||||
if (!setEmail.Succeeded)
|
||||
errors.AddRange(setEmail.Errors.Select(e => e.Description));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(request.NewPassword))
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.CurrentPassword))
|
||||
return ServiceResult.Failure("Current password is required to set a new password.");
|
||||
|
||||
var changePassword = await users.ChangePasswordAsync(
|
||||
user, request.CurrentPassword, request.NewPassword);
|
||||
|
||||
if (!changePassword.Succeeded)
|
||||
errors.AddRange(changePassword.Errors.Select(e => e.Description));
|
||||
}
|
||||
|
||||
if (errors.Count > 0)
|
||||
return ServiceResult.Failure(errors);
|
||||
|
||||
await signIn.RefreshSignInAsync(user);
|
||||
return ServiceResult.Ok();
|
||||
}
|
||||
|
||||
public async Task<ServiceResult> DeleteAccountAsync(string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await users.FindByIdAsync(userId);
|
||||
if (user is null)
|
||||
return ServiceResult.Failure("User not found.");
|
||||
|
||||
await signIn.SignOutAsync();
|
||||
var result = await users.DeleteAsync(user);
|
||||
return result.Succeeded
|
||||
? ServiceResult.Ok()
|
||||
: ServiceResult.Failure(result.Errors.Select(e => e.Description));
|
||||
}
|
||||
|
||||
public async Task<UserDataExportDto?> ExportUserDataAsync(string userId, CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await users.FindByIdAsync(userId);
|
||||
if (user is null)
|
||||
return null;
|
||||
|
||||
var roles = await users.GetRolesAsync(user);
|
||||
return new UserDataExportDto(
|
||||
user.Id,
|
||||
user.UserName!,
|
||||
user.Email!,
|
||||
roles.ToList(),
|
||||
DateTime.UtcNow);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
using Eshop.Application.Auth;
|
||||
using Eshop.Application.Common;
|
||||
using Eshop.Infrastructure.Persistence;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Eshop.Infrastructure.Identity;
|
||||
|
||||
public class AspNetIdentityUserAdministrationService(
|
||||
UserManager<UserEntity> users,
|
||||
RoleManager<IdentityRole> roles) : IUserAdministrationService
|
||||
{
|
||||
public async Task<IReadOnlyList<UserWithRolesDto>> GetAllUsersWithRolesAsync(
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var allRoles = roles.Roles.Select(r => r.Name!).ToList();
|
||||
var viewModels = new List<UserWithRolesDto>();
|
||||
|
||||
foreach (var user in users.Users.ToList())
|
||||
{
|
||||
var userRoles = await users.GetRolesAsync(user);
|
||||
viewModels.Add(ToDto(user, userRoles, allRoles));
|
||||
}
|
||||
|
||||
return viewModels;
|
||||
}
|
||||
|
||||
public async Task<UserWithRolesDto?> GetUserWithRolesAsync(
|
||||
string userId,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await users.FindByIdAsync(userId);
|
||||
if (user is null)
|
||||
return null;
|
||||
|
||||
var userRoles = await users.GetRolesAsync(user);
|
||||
var allRoles = roles.Roles.Select(r => r.Name!).ToList();
|
||||
return ToDto(user, userRoles, allRoles);
|
||||
}
|
||||
|
||||
public async Task<ServiceResult> UpdateUserRolesAsync(
|
||||
string userId,
|
||||
IReadOnlyList<string> selectedRoles,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var user = await users.FindByIdAsync(userId);
|
||||
if (user is null)
|
||||
return ServiceResult.Failure("User not found.");
|
||||
|
||||
var currentRoles = await users.GetRolesAsync(user);
|
||||
await users.RemoveFromRolesAsync(user, currentRoles);
|
||||
|
||||
if (selectedRoles.Count <= 0) return ServiceResult.Ok();
|
||||
|
||||
var result = await users.AddToRolesAsync(user, selectedRoles);
|
||||
return !result.Succeeded ? ServiceResult.Failure(result.Errors.Select(e => e.Description)) : ServiceResult.Ok();
|
||||
}
|
||||
|
||||
private static UserWithRolesDto ToDto(
|
||||
UserEntity user,
|
||||
IList<string> userRoles,
|
||||
IList<string> allRoles) =>
|
||||
new(
|
||||
new UserDto(user.Id, user.UserName!, user.Email!),
|
||||
userRoles.ToList(),
|
||||
allRoles.ToList());
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Eshop.Infrastructure.Persistence;
|
||||
|
||||
public class UserEntity : IdentityUser
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user