first commit

This commit is contained in:
fa0xh1 2024-06-05 22:14:10 +07:00
commit 7a653f6d34
397 changed files with 25415 additions and 0 deletions

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

51
MiniSkeletonAPI.sln Normal file
View File

@ -0,0 +1,51 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{FDCD7C52-B43C-4CA4-9D30-7AB851951E39}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{F54A3A88-80CB-4D9C-9ACD-A14A82876FAA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSkeletonAPI.Application", "src\core\MiniSkeletonAPI.Application\MiniSkeletonAPI.Application.csproj", "{ACB30A63-8B21-45E6-89D9-BE794FA492D2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSkeletonAPI.Domain", "src\core\MiniSkeletonAPI.Domain\MiniSkeletonAPI.Domain.csproj", "{11E7C90E-B5C9-4F64-BA12-DB7DBB4D4FB8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSkeletonAPI.Infrastructure", "src\MiniSkeletonAPI.Infrastructure\MiniSkeletonAPI.Infrastructure.csproj", "{A2AD3474-503B-436D-A521-CDB88E3C490A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MiniSkeletonAPI.Presentation", "src\MiniSkeletonAPI.Presentation\MiniSkeletonAPI.Presentation.csproj", "{E02CEC6A-A021-4B66-8F10-B154943A359A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{ACB30A63-8B21-45E6-89D9-BE794FA492D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{ACB30A63-8B21-45E6-89D9-BE794FA492D2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{ACB30A63-8B21-45E6-89D9-BE794FA492D2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{ACB30A63-8B21-45E6-89D9-BE794FA492D2}.Release|Any CPU.Build.0 = Release|Any CPU
{11E7C90E-B5C9-4F64-BA12-DB7DBB4D4FB8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{11E7C90E-B5C9-4F64-BA12-DB7DBB4D4FB8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{11E7C90E-B5C9-4F64-BA12-DB7DBB4D4FB8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{11E7C90E-B5C9-4F64-BA12-DB7DBB4D4FB8}.Release|Any CPU.Build.0 = Release|Any CPU
{A2AD3474-503B-436D-A521-CDB88E3C490A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2AD3474-503B-436D-A521-CDB88E3C490A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2AD3474-503B-436D-A521-CDB88E3C490A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A2AD3474-503B-436D-A521-CDB88E3C490A}.Release|Any CPU.Build.0 = Release|Any CPU
{E02CEC6A-A021-4B66-8F10-B154943A359A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E02CEC6A-A021-4B66-8F10-B154943A359A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E02CEC6A-A021-4B66-8F10-B154943A359A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E02CEC6A-A021-4B66-8F10-B154943A359A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F54A3A88-80CB-4D9C-9ACD-A14A82876FAA} = {FDCD7C52-B43C-4CA4-9D30-7AB851951E39}
{ACB30A63-8B21-45E6-89D9-BE794FA492D2} = {F54A3A88-80CB-4D9C-9ACD-A14A82876FAA}
{11E7C90E-B5C9-4F64-BA12-DB7DBB4D4FB8} = {F54A3A88-80CB-4D9C-9ACD-A14A82876FAA}
{A2AD3474-503B-436D-A521-CDB88E3C490A} = {FDCD7C52-B43C-4CA4-9D30-7AB851951E39}
{E02CEC6A-A021-4B66-8F10-B154943A359A} = {FDCD7C52-B43C-4CA4-9D30-7AB851951E39}
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,66 @@
using AutoMapper;
using MiniSkeletonAPI.Application.Common.Interfaces;
using MiniSkeletonAPI.Application.Identity.Roles.Queries.GetRolesWithPagination;
using MiniSkeletonAPI.Application.Identity.Users.Queries.GetUsersWithPagination;
using MiniSkeletonAPI.Domain.Entities;
using MiniSkeletonAPI.Infrastructure.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
namespace MiniSkeletonAPI.Application.Common.Mappings
{
public class MappingProfile : Profile
{
public MappingProfile()
{
ApplyMappingsFromAssembly(Assembly.GetExecutingAssembly());
CreateMap<ApplicationUser, UserBriefDto>();
CreateMap<ApplicationRole, RoleBriefDto>();
}
private void ApplyMappingsFromAssembly(Assembly assembly)
{
var mapFromType = typeof(IMapFrom<>);
var mappingMethodName = nameof(IMapFrom<object>.Mapping);
bool HasInterface(Type t) => t.IsGenericType && t.GetGenericTypeDefinition() == mapFromType;
var types = assembly.GetExportedTypes().Where(t => t.GetInterfaces().Any(HasInterface)).ToList();
var argumentTypes = new Type[] { typeof(Profile) };
foreach (var type in types)
{
var instance = Activator.CreateInstance(type);
var methodInfo = type.GetMethod(mappingMethodName);
if (methodInfo != null)
{
methodInfo.Invoke(instance, new object[] { this });
}
else
{
var interfaces = type.GetInterfaces().Where(HasInterface).ToList();
if (interfaces.Count > 0)
{
foreach (var @interface in interfaces)
{
var interfaceMethodInfo = @interface.GetMethod(mappingMethodName, argumentTypes);
interfaceMethodInfo.Invoke(instance, new object[] { this });
}
}
}
}
}
}
}

View File

@ -0,0 +1,39 @@
using System.Reflection;
using System.Reflection.Emit;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
using MiniSkeletonAPI.Application.Common.Interfaces;
using MiniSkeletonAPI.Domain.Entities;
using MiniSkeletonAPI.Infrastructure.Identity;
namespace MiniSkeletonAPI.Infrastructure.Data;
public class ApplicationDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserClaim<string>,
ApplicationUserRole, IdentityUserLogin<string>,
IdentityRoleClaim<string>, IdentityUserToken<string>>, IApplicationDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }
public DbSet<TodoList> TodoLists => Set<TodoList>();
public DbSet<TodoItem> TodoItems => Set<TodoItem>();
public DbSet<ApplicationRole> Roles => Set<ApplicationRole>();
public DbSet<ApplicationUserRole> UserRoles => Set<ApplicationUserRole>();
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<ApplicationUserRole>(userRole =>
{
userRole.HasKey(ur => new { ur.UserId, ur.RoleId });
userRole.HasOne(ur => ur.Role)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.RoleId);
userRole.HasOne(ur => ur.User)
.WithMany(r => r.UserRoles)
.HasForeignKey(ur => ur.UserId);
});
builder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
}

View File

@ -0,0 +1,131 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MiniSkeletonAPI.Infrastructure.Identity;
using MiniSkeletonAPI.Domain.Entities;
using MiniSkeletonAPI.Domain.Constants;
using MiniSkeletonAPI.Infrastructure.Identity.Permission;
using System.Security.Claims;
namespace MiniSkeletonAPI.Infrastructure.Data;
public static class InitialiserExtensions
{
public static async Task InitialiseDatabaseAsync(this WebApplication app)
{
using var scope = app.Services.CreateScope();
var initialiser = scope.ServiceProvider.GetRequiredService<ApplicationDbContextInitialiser>();
await initialiser.InitialiseAsync();
await initialiser.SeedAsync();
}
}
public class ApplicationDbContextInitialiser
{
private readonly ILogger<ApplicationDbContextInitialiser> _logger;
private readonly ApplicationDbContext _context;
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
public ApplicationDbContextInitialiser(ILogger<ApplicationDbContextInitialiser> logger, ApplicationDbContext context, UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)
{
_logger = logger;
_context = context;
_userManager = userManager;
_roleManager = roleManager;
}
public async Task InitialiseAsync()
{
try
{
await _context.Database.MigrateAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while initialising the database.");
throw;
}
}
public async Task SeedAsync()
{
try
{
await TrySeedAsync();
}
catch (Exception ex)
{
_logger.LogError(ex, "An error occurred while seeding the database.");
throw;
}
}
public async Task TrySeedAsync()
{
// Default roles
var administratorRole = new ApplicationRole(Roles.Administrator);
var userRole = new ApplicationRole(Roles.User);
if (_roleManager.Roles.All(r => r.Name != administratorRole.Name))
{
await _roleManager.CreateAsync(administratorRole);
}
if (_roleManager.Roles.All(r => r.Name != userRole.Name))
{
await _roleManager.CreateAsync(userRole);
}
// Default users
var administrator = new ApplicationUser { UserName = "administrator@localhost", Email = "administrator@localhost", Last_Created = new DateTime().ToUniversalTime() };
var user1 = new ApplicationUser { UserName = "sanji", Email = "sanji@localhost", Last_Created = DateTime.Now };
if (_userManager.Users.All(u => u.UserName != administrator.UserName))
{
await _userManager.CreateAsync(administrator, "Administrator1!");
if (!string.IsNullOrWhiteSpace(administratorRole.Name))
{
await _userManager.AddToRolesAsync(administrator, new[] { administratorRole.Name });
}
}
if (_userManager.Users.All(u => u.UserName != user1.UserName))
{
await _userManager.CreateAsync(user1, "Administrator1!");
//if (!string.IsNullOrWhiteSpace(administratorRole.Name))
//{
//}
}
var userClaims = await _userManager.FindByNameAsync(user1.UserName);
await _userManager.AddClaimAsync(userClaims, new Claim(CustomClaimTypes.Permission, Permissions.Dashboards.View));
//await _roleManager.CreateAsync(new IdentityRole("Administrators"));
var adminRole = await _roleManager.FindByNameAsync("Administrator");
//await _roleManager.AddClaimAsync(adminRole, new Claim(CustomClaimTypes.Permission, Permissions.Dashboards.View));
//await _roleManager.AddClaimAsync(adminRole, new Claim(CustomClaimTypes.Permission, Permissions.Dashboards.Create));
// Default data
// Seed, if necessary
if (!_context.TodoLists.Any())
{
_context.TodoLists.Add(new TodoList
{
Title = "Todo List",
Items =
{
new TodoItem { Title = "Make a todo list 📃" },
new TodoItem { Title = "Check off the first item ✅" },
new TodoItem { Title = "Realise you've already done two things on the list! 🤯"},
new TodoItem { Title = "Reward yourself with a nice, long nap 🏆" },
}
});
await _context.SaveChangesAsync();
}
}
}

View File

@ -0,0 +1,15 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using MiniSkeletonAPI.Domain.Entities;
namespace MiniSkeletonAPI.Infrastructure.Data.Configurations;
public class TodoItemConfiguration : IEntityTypeConfiguration<TodoItem>
{
public void Configure(EntityTypeBuilder<TodoItem> builder)
{
builder.Property(t => t.Title)
.HasMaxLength(200)
.IsRequired();
}
}

View File

@ -0,0 +1,18 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using MiniSkeletonAPI.Domain.Entities;
namespace MiniSkeletonAPI.Infrastructure.Data.Configurations;
public class TodoListConfiguration : IEntityTypeConfiguration<TodoList>
{
public void Configure(EntityTypeBuilder<TodoList> builder)
{
builder.Property(t => t.Title)
.HasMaxLength(200)
.IsRequired();
//builder
// .OwnsOne(b => b.Colour);
}
}

View File

@ -0,0 +1,64 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using MiniSkeletonAPI.Domain.Common;
using MiniSkeletonAPI.Infrastructure.Data.Interceptors;
namespace MiniSkeletonAPI.Infrastructure.Data.Interceptors;
public class AuditableEntityInterceptor : SaveChangesInterceptor
{
//private readonly IUser _user;
private readonly TimeProvider _dateTime;
public AuditableEntityInterceptor(
//IUser user,
TimeProvider dateTime)
{
//_user = user;
_dateTime = dateTime;
}
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
UpdateEntities(eventData.Context);
return base.SavingChanges(eventData, result);
}
public override ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
UpdateEntities(eventData.Context);
return base.SavingChangesAsync(eventData, result, cancellationToken);
}
public void UpdateEntities(DbContext? context)
{
if (context == null) return;
foreach (var entry in context.ChangeTracker.Entries<BaseAuditableEntity>())
{
if (entry.State is EntityState.Added or EntityState.Modified || entry.HasChangedOwnedEntities())
{
var utcNow = _dateTime.GetUtcNow();
if (entry.State == EntityState.Added)
{
entry.Entity.CreatedBy = "John Doe";
entry.Entity.Created = utcNow;
}
entry.Entity.LastModifiedBy = "John Doe";
entry.Entity.LastModified = utcNow;
}
}
}
}
public static class Extensions
{
public static bool HasChangedOwnedEntities(this EntityEntry entry) =>
entry.References.Any(r =>
r.TargetEntry != null &&
r.TargetEntry.Metadata.IsOwned() &&
(r.TargetEntry.State == EntityState.Added || r.TargetEntry.State == EntityState.Modified));
}

View File

@ -0,0 +1,50 @@
using MediatR;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using MiniSkeletonAPI.Domain.Common;
namespace MiniSkeletonAPI.Infrastructure.Data.Interceptors;
public class DispatchDomainEventsInterceptor : SaveChangesInterceptor
{
private readonly IMediator _mediator;
public DispatchDomainEventsInterceptor(IMediator mediator)
{
_mediator = mediator;
}
public override InterceptionResult<int> SavingChanges(DbContextEventData eventData, InterceptionResult<int> result)
{
DispatchDomainEvents(eventData.Context).GetAwaiter().GetResult();
return base.SavingChanges(eventData, result);
}
public override async ValueTask<InterceptionResult<int>> SavingChangesAsync(DbContextEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
await DispatchDomainEvents(eventData.Context);
return await base.SavingChangesAsync(eventData, result, cancellationToken);
}
public async Task DispatchDomainEvents(DbContext? context)
{
if (context == null) return;
var entities = context.ChangeTracker
.Entries<BaseEntity>()
.Where(e => e.Entity.DomainEvents.Any())
.Select(e => e.Entity);
var domainEvents = entities
.SelectMany(e => e.DomainEvents)
.ToList();
entities.ToList().ForEach(e => e.ClearDomainEvents());
foreach (var domainEvent in domainEvents)
await _mediator.Publish(domainEvent);
}
}

View File

@ -0,0 +1,176 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using MiniSkeletonAPI.Infrastructure.Data.Interceptors;
using MiniSkeletonAPI.Infrastructure.Data;
using MiniSkeletonAPI.Application.Common.Interfaces;
using MiniSkeletonAPI.Infrastructure.Identity;
using MiniSkeletonAPI.Domain.Constants;
using MiniSkeletonAPI.Infrastructure.Identity.Permission;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using System.Reflection;
namespace MiniSkeletonAPI.Infrastructure
{
public class AppClaimsPrincipalFactory : UserClaimsPrincipalFactory<ApplicationUser, ApplicationRole>
{
public AppClaimsPrincipalFactory(UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager, IOptions<IdentityOptions> optionsAccessor)
: base(userManager, roleManager, optionsAccessor)
{
}
public override async Task<ClaimsPrincipal> CreateAsync(ApplicationUser user)
{
if (user == null)
{
throw new ArgumentNullException(nameof(user));
}
var userId = await UserManager.GetUserIdAsync(user);
var userName = await UserManager.GetUserNameAsync(user);
var id = new ClaimsIdentity("Identity.Application",
Options.ClaimsIdentity.UserNameClaimType,
Options.ClaimsIdentity.RoleClaimType);
id.AddClaim(new Claim(Options.ClaimsIdentity.UserIdClaimType, userId));
id.AddClaim(new Claim(Options.ClaimsIdentity.UserNameClaimType, userName));
if (UserManager.SupportsUserSecurityStamp)
{
id.AddClaim(new Claim(Options.ClaimsIdentity.SecurityStampClaimType,
await UserManager.GetSecurityStampAsync(user)));
}
// code removed that adds the role claims
if (UserManager.SupportsUserClaim)
{
id.AddClaims(await UserManager.GetClaimsAsync(user));
}
return new ClaimsPrincipal(id);
}
}
public class MyDataProtector : IDataProtector
{
public IDataProtector CreateProtector(string purpose)
{
return new MyDataProtector();
}
public byte[] Protect(byte[] plaintext)
{
return plaintext;
}
public byte[] Unprotect(byte[] protectedData)
{
return protectedData;
}
}
public static class DependencyInjection
{
public static void ConfigureJWT(this IServiceCollection services, IConfiguration
configuration)
{
var jwtSettings = configuration.GetSection("JwtSettings");
var secretKey = configuration.GetConnectionString("Secret");
services.AddAuthentication(opt =>
{
opt.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = jwtSettings["validIssuer"],
ValidAudience = jwtSettings["validAudience"],
IssuerSigningKey = new
SymmetricSecurityKey(Encoding.UTF8.GetBytes(secretKey))
};
});
}
public static IServiceCollection AddInfrastructureServices(this IServiceCollection services, IConfiguration configuration)
{
var connectionString = configuration.GetConnectionString("DefaultConnection");
//Guard.Against.Null(connectionString, message: "Connection string 'DefaultConnection' not found.");
services.AddScoped<ISaveChangesInterceptor, AuditableEntityInterceptor>();
services.AddScoped<ISaveChangesInterceptor, DispatchDomainEventsInterceptor>();
services.AddDbContext<ApplicationDbContext>((sp, options) =>
{
options.AddInterceptors(sp.GetServices<ISaveChangesInterceptor>());
options.UseSqlite(connectionString);
});
services.AddAutoMapper(Assembly.GetExecutingAssembly());
services.AddScoped<IApplicationDbContext>(provider => provider.GetRequiredService<ApplicationDbContext>());
services.AddScoped<ApplicationDbContextInitialiser>();
//services.ConfigureJWT(configuration);
services.AddAuthentication()
//.AddJwtBearer();
.AddBearerToken(IdentityConstants.BearerScheme
, o =>
{
o.BearerTokenProtector = new TicketDataFormat(
new MyDataProtector()
.CreateProtector(""));
o.RefreshTokenProtector = new TicketDataFormat(
new MyDataProtector()
.CreateProtector(""));
}
);
services.AddAuthorizationBuilder();
services.AddScoped<IAuthorizationHandler, PermissionAuthorizationHandler>();
services.AddSingleton<IAuthorizationPolicyProvider, PermissionPolicyProvider>();
services.Configure<IdentityOptions>(x =>
{
x.Password.RequireDigit = false;
x.Password.RequiredLength = 2;
x.Password.RequireUppercase = false;
x.Password.RequireLowercase = false;
x.Password.RequireNonAlphanumeric = false;
x.Password.RequiredUniqueChars = 0;
x.Lockout.AllowedForNewUsers = true;
x.Lockout.MaxFailedAccessAttempts = 5;
x.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromSeconds(30);
});
services
.AddIdentityCore<ApplicationUser>(opt => {
})
.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddApiEndpoints();
services.AddScoped<IUserClaimsPrincipalFactory<ApplicationUser>, AppClaimsPrincipalFactory>();
services.AddSingleton(TimeProvider.System);
services.AddTransient<IIdentityService, IdentityService>();
services.AddAuthorization(options =>
options.AddPolicy(Policies.CanPurge, policy => policy.RequireRole(Roles.Administrator)));
return services;
}
}
}

View File

@ -0,0 +1 @@


View File

@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MiniSkeletonAPI.Infrastructure.Identity
{
public class ApplicationPermissionClaim : IdentityRoleClaim<string>
{
public DateTime Last_Created { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MiniSkeletonAPI.Infrastructure.Identity
{
public class ApplicationRole : IdentityRole
{
public ApplicationRole(string Name)
: base(Name) { }
public ICollection<ApplicationUserRole> UserRoles { get; set; }
public DateTime? LastCreated { get; set; }
[NotMapped]
public List<ApplicationPermissionClaim> Claims { get; set; }
}
}

View File

@ -0,0 +1,27 @@
using AutoMapper;
using Microsoft.AspNetCore.Identity;
using MiniSkeletonAPI.Application.Identity.Users.Queries.GetUsersWithPagination;
using MiniSkeletonAPI.Domain.Entities;
using System.ComponentModel.DataAnnotations.Schema;
namespace MiniSkeletonAPI.Infrastructure.Identity;
public class ApplicationUser : IdentityUser
{
//public required string Username { get; set; };
public DateTime Last_Created { get; set; }
public string? RefreshToken { get; set; }
public DateTime RefreshTokenExpiryTime { get; set; }
[NotMapped]
public ICollection<string>? Roles { get; set; }
public ICollection<ApplicationUserRole>? UserRoles { get; set; }
private class Mapping : Profile
{
public Mapping()
{
CreateMap<ApplicationUser, User>();
CreateMap<ApplicationUser, UserBriefDto>();
}
}
}

View File

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MiniSkeletonAPI.Infrastructure.Identity
{
public class ApplicationUserRole : IdentityUserRole<string>
{
public virtual ApplicationUser User { get; set; }
public virtual ApplicationRole Role { get; set; }
}
}

View File

@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Identity;
using MiniSkeletonAPI.Application.Common.Models;
namespace MiniSkeletonAPI.Infrastructure.Identity;
public static class IdentityResultExtensions
{
public static Result ToApplicationResult(this IdentityResult result)
{
return result.Succeeded
? Result.Success()
: Result.Failure(result.Errors.Select(e => e.Description));
}
}

View File

@ -0,0 +1,214 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using MiniSkeletonAPI.Application.Common.Models;
using MiniSkeletonAPI.Infrastructure.Identity;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using MiniSkeletonAPI.Application.Common.Interfaces;
using MiniSkeletonAPI.Domain.Entities;
using MiniSkeletonAPI.Infrastructure.Data;
using System.Text.Json;
using static System.Runtime.InteropServices.JavaScript.JSType;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using MiniSkeletonAPI.Application.Common.Mappings;
using MiniSkeletonAPI.Application.Identity.Users.Queries.GetUsersWithPagination;
using MiniSkeletonAPI.Application.Identity.Roles.Queries.GetRolesWithPagination;
namespace MiniSkeletonAPI.Infrastructure.Identity;
public class IdentityService : IIdentityService
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<ApplicationRole> _roleManager;
private readonly IUserClaimsPrincipalFactory<ApplicationUser> _userClaimsPrincipalFactory;
private readonly IAuthorizationService _authorizationService;
private readonly ApplicationDbContext _context;
private readonly IMapper _mapper;
public IdentityService(
UserManager<ApplicationUser> userManager,
ApplicationDbContext context,
RoleManager<ApplicationRole> roleManager,
IUserClaimsPrincipalFactory<ApplicationUser> userClaimsPrincipalFactory,
IMapper mapper,
IAuthorizationService authorizationService)
{
_userManager = userManager;
_roleManager = roleManager;
_context = context;
_userClaimsPrincipalFactory = userClaimsPrincipalFactory;
_authorizationService = authorizationService;
_mapper = mapper;
}
public async Task<string?> GetUserNameAsync(string userId)
{
var user = await _userManager.FindByIdAsync(userId);
return user?.UserName;
}
public async Task<User?> GetByIdAsync(string userId)
{
var userEntity = await _userManager.FindByIdAsync(userId);
var user = new User
{
UserName = userEntity.UserName,
Email = userEntity.Email,
PhoneNumber = userEntity.PhoneNumber
};
return user;
}
public async Task<PaginatedList<UserBriefDto>> GetUsersPaginatedAsync(GetUsersWithPaginationQuery request)
{
return await _userManager.Users
//.Where(x => x. == request)
.OrderBy(x => x.Last_Created)
.ProjectTo<UserBriefDto>(_mapper.ConfigurationProvider)
.PaginatedListAsync(request.PageNumber, request.PageSize);
}
public async Task<(Result Result, string UserId)> CreateUserAsync(User user,string password)
{
var userEntity = new ApplicationUser
{
UserName = user.UserName,
Email = user.Email
};
var result = await _userManager.CreateAsync(userEntity, password);
if(result.Succeeded)
{
await Console.Out.WriteLineAsync("Sukses");
return (Result.Success(), userEntity.Id);
}
{
List<IdentityError> errorList = result.Errors.ToList();
var errors = string.Join(", ", errorList.Select(e => e.Description));
return (Result.Failure(errors), userEntity.Id);
}
}
public async Task<(Result Result, string UserId)> UpdateUserAsync(User user,string userId)
{
var userEntity = await _userManager.FindByIdAsync(userId);
userEntity.PhoneNumber = user.PhoneNumber;
userEntity.UserName = user.UserName;
var result = await _userManager.UpdateAsync(userEntity);
if (result.Succeeded)
{
await _userManager.RemovePasswordAsync(userEntity);
await _userManager.AddPasswordAsync(userEntity, user.Password);
return (Result.Success(), userEntity.Id);
}
else
{
List<IdentityError> errorList = result.Errors.ToList();
var errors = string.Join(", ", errorList.Select(e => e.Description));
return (Result.Failure(errors), userEntity.Id);
}
}
public async Task<bool> IsInRoleAsync(string userId, string role)
{
var user = await _userManager.FindByIdAsync(userId);
return user != null && await _userManager.IsInRoleAsync(user, role);
}
public async Task<bool> AuthorizeAsync(string userId, string policyName)
{
var user = await _userManager.FindByIdAsync(userId);
if (user == null)
{
return false;
}
var principal = await _userClaimsPrincipalFactory.CreateAsync(user);
var result = await _authorizationService.AuthorizeAsync(principal, policyName);
return result.Succeeded;
}
public async Task<Result> DeleteUserAsync(string userId)
{
var user = await _userManager.FindByIdAsync(userId);
return user != null ? await DeleteUserAsync(user) : Result.Success();
}
public async Task<Result> DeleteUserAsync(ApplicationUser user)
{
var result = await _userManager.DeleteAsync(user);
return result.ToApplicationResult();
}
public async Task<PaginatedList<RoleBriefDto>> GetRolesPaginatedAsync(GetRolesWithPaginationQuery request)
{
return await _roleManager.Roles
//.Where(x => x. == request)
.OrderBy(x => x.LastCreated)
.ProjectTo<RoleBriefDto>(_mapper.ConfigurationProvider)
.PaginatedListAsync(request.PageNumber, request.PageSize);
}
public async Task<(Result Result, string RoleId)> CreateRoleAsync(Role role)
{
var entity = new ApplicationRole(role.Name)
{
LastCreated = new DateTime()
};
var result = await _roleManager.CreateAsync(entity);
if (result.Succeeded)
{
return (Result.Success(), entity.Id);
}
{
List<IdentityError> errorList = result.Errors.ToList();
var errors = string.Join(", ", errorList.Select(e => e.Description));
return (Result.Failure(errors), entity.Id);
}
}
public async Task<(Result Result, string RoleId)> UpdateRoleAsync(Role role, string roleId)
{
var entity = await _roleManager.FindByIdAsync(roleId);
entity.Name = role.Name;
var result = await _roleManager.UpdateAsync(entity);
if (result.Succeeded)
{
//await _userManager.RemovePasswordAsync(entity);
//await _userManager.AddPasswordAsync(entity, user.Password);
return (Result.Success(), entity.Id);
}
else
{
List<IdentityError> errorList = result.Errors.ToList();
var errors = string.Join(", ", errorList.Select(e => e.Description));
return (Result.Failure(errors), entity.Id);
}
}
public async Task<Result> DeleteRoleAsync(string roleId)
{
var role = await _roleManager.FindByIdAsync(roleId);
return role != null ? await DeleteRoleAsync(role) : Result.Success();
}
public async Task<Result> DeleteRoleAsync(ApplicationRole role)
{
var result = await _roleManager.DeleteAsync(role);
return result.ToApplicationResult();
}
}

View File

@ -0,0 +1,87 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
namespace MiniSkeletonAPI.Infrastructure.Identity.Permission
{
//public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
//{
// public PermissionAuthorizationHandler() { }
// protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
// {
// if (context.User.Identity != null && !context.User.Identity.IsAuthenticated || context.User.Identity == null)
// {
// context.Fail();
// //context.Succeed(requirement);
// return;
// }
// if (requirement.Permission == null)
// {
// context.Succeed(requirement);
// return;
// }
// var permissionss = context.User.Claims.Where(x => x.Type == "Permission" &&
// x.Value == requirement.Permission &&
// x.Issuer == "SkeletonAPI");
// if (permissionss.Any())
// {
// context.Succeed(requirement);
// return;
// }
// }
//}
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{
UserManager<ApplicationUser> _userManager;
RoleManager<ApplicationRole> _roleManager;
public PermissionAuthorizationHandler(UserManager<ApplicationUser> userManager, RoleManager<ApplicationRole> roleManager)
{
_userManager = userManager;
_roleManager = roleManager;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
{
if (context.User == null)
{
return;
}
// Get all the roles the user belongs to and check if any of the roles has the permission required
// for the authorization to succeed.
var permissions = new List<string>();
var user = await _userManager.GetUserAsync(context.User);
//await Console.Out.WriteLineAsync(JsonSerializer.Serialize(user));
var userRoleNames = await _userManager.GetRolesAsync(user);
var userClaimNames = await _userManager.GetClaimsAsync(user);
var userRoles = _roleManager.Roles.Where(x => userRoleNames.Contains(x.Name));
//await Console.Out.WriteLineAsync(JsonSerializer.Serialize(userRoles));
permissions.AddRange(userClaimNames.Select(x=>x.Value));
foreach (var role in userRoles)
{
var roleClaims = await _roleManager.GetClaimsAsync(role);
permissions.AddRange(roleClaims.Where(x => x.Type == CustomClaimTypes.Permission &&
x.Value == requirement.Permission &&
x.Issuer == "LOCAL AUTHORITY")
.Select(x => x.Value));
//await Console.Out.WriteLineAsync(JsonSerializer.Serialize(roleClaims));
//await Console.Out.WriteLineAsync(JsonSerializer.Serialize(permissions));
}
if (permissions.Any())
{
context.Succeed(requirement);
return;
}
}
}
}

View File

@ -0,0 +1,43 @@
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using System;
using System.Threading.Tasks;
namespace MiniSkeletonAPI.Infrastructure.Identity.Permission
{
public class PermissionPolicyProvider : IAuthorizationPolicyProvider
{
public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
{
// There can only be one policy provider in ASP.NET Core.
// We only handle permissions related policies, for the rest
/// we will use the default provider.
FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider(options);
}
public Task<AuthorizationPolicy> GetDefaultPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
// Dynamically creates a policy with a requirement that contains the permission.
// The policy name must match the permission that is needed.
public Task<AuthorizationPolicy> GetPolicyAsync(string policyName)
{
if (policyName.StartsWith("Permission", StringComparison.OrdinalIgnoreCase))
{
var policy = new AuthorizationPolicyBuilder();
policy.AddRequirements(new PermissionRequirement(policyName));
return Task.FromResult(policy.Build());
}
// Policy is not for permissions, try the default provider.
return FallbackPolicyProvider.GetPolicyAsync(policyName);
}
//public Task<AuthorizationPolicy> GetFallbackPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
public Task<AuthorizationPolicy> GetFallbackPolicyAsync()
{
return Task.FromResult<AuthorizationPolicy>(null);
}
}
}

View File

@ -0,0 +1,14 @@
using Microsoft.AspNetCore.Authorization;
namespace MiniSkeletonAPI.Infrastructure.Identity.Permission
{
public class PermissionRequirement : IAuthorizationRequirement
{
public string Permission { get; private set; }
public PermissionRequirement(string permission)
{
Permission = permission;
}
}
}

View File

@ -0,0 +1,43 @@
namespace MiniSkeletonAPI.Infrastructure.Identity.Permission
{
public static class Permissions
{
//public static List<string> GeneratePermissionsForModule(string module)
//{
// return new List<string>()
// {
// $"Permissions.{module}.Create",
// $"Permissions.{module}.View",
// $"Permissions.{module}.Edit",
// $"Permissions.{module}.Delete",
// };
//}
public static class Dashboards
{
public const string View = "Permissions.Dashboards.View";
public const string Create = "Permissions.Dashboards.Create";
public const string Edit = "Permissions.Dashboards.Edit";
public const string Delete = "Permissions.Dashboards.Delete";
}
public static class Users
{
public const string View = "Permissions.Users.View";
public const string Create = "Permissions.Users.Create";
public const string Edit = "Permissions.Users.Edit";
public const string Delete = "Permissions.Users.Delete";
}
//public static class Products
//{
// public const string View = "Permissions.Products.View";
// public const string Create = "Permissions.Products.Create";
// public const string Edit = "Permissions.Products.Edit";
// public const string Delete = "Permissions.Products.Delete";
//}
}
public class CustomClaimTypes
{
public const string Permission = "Permission";
}
}

View File

@ -0,0 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.6" />
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="8.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.5" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\core\MiniSkeletonAPI.Application\MiniSkeletonAPI.Application.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,19 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.0"
},
{
"name": "Microsoft.AspNetCore.App",
"version": "8.0.0"
}
],
"configProperties": {
"System.Reflection.NullabilityInfoContext.IsSupported": true,
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
}

View File

@ -0,0 +1,4 @@
// <autogenerated />
using System;
using System.Reflection;
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]

View File

@ -0,0 +1,23 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("MiniSkeletonAPI.Infrastructure")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
[assembly: System.Reflection.AssemblyProductAttribute("MiniSkeletonAPI.Infrastructure")]
[assembly: System.Reflection.AssemblyTitleAttribute("MiniSkeletonAPI.Infrastructure")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class.

View File

@ -0,0 +1 @@
955e66488cb04eb98005da0fb6c4021b5e7ca317d6e0f2985650912d716e46aa

View File

@ -0,0 +1,13 @@
is_global = true
build_property.TargetFramework = net8.0
build_property.TargetPlatformMinVersion =
build_property.UsingMicrosoftNETSdkWeb =
build_property.ProjectTypeGuids =
build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules =
build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = MiniSkeletonAPI.Infrastructure
build_property.ProjectDir = D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\
build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop =

View File

@ -0,0 +1,8 @@
// <auto-generated/>
global using global::System;
global using global::System.Collections.Generic;
global using global::System.IO;
global using global::System.Linq;
global using global::System.Net.Http;
global using global::System.Threading;
global using global::System.Threading.Tasks;

View File

@ -0,0 +1 @@
31ec96ef62c2578f82b3ffb599b310d4a2b9a9ca98a54ccedcec732bf6534c94

View File

@ -0,0 +1,19 @@
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Infrastructure.deps.json
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Infrastructure.runtimeconfig.json
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Infrastructure.dll
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Infrastructure.pdb
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Application.dll
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Domain.dll
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Application.pdb
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\bin\Debug\net8.0\MiniSkeletonAPI.Domain.pdb
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.csproj.AssemblyReference.cache
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.GeneratedMSBuildEditorConfig.editorconfig
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.AssemblyInfoInputs.cache
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.AssemblyInfo.cs
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.csproj.CoreCompileInputs.cache
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkel.949037F5.Up2Date
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.dll
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\refint\MiniSkeletonAPI.Infrastructure.dll
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.pdb
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\MiniSkeletonAPI.Infrastructure.genruntimeconfig.cache
D:\DevPT3\MiniSkeletonAPI\src\MiniSkeletonAPI.Infrastructure\obj\Debug\net8.0\ref\MiniSkeletonAPI.Infrastructure.dll

View File

@ -0,0 +1 @@
a1f0c67468e8d76250a2a3fc99ee2180f5bfabc358e32d63fdd7e9aaaec14440

View File

@ -0,0 +1,269 @@
{
"format": 1,
"restore": {
"D:\\DevPT3\\MiniSkeletonAPI\\src\\MiniSkeletonAPI.Infrastructure\\MiniSkeletonAPI.Infrastructure.csproj": {}
},
"projects": {
"D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Application\\MiniSkeletonAPI.Application.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Application\\MiniSkeletonAPI.Application.csproj",
"projectName": "MiniSkeletonAPI.Application",
"projectPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Application\\MiniSkeletonAPI.Application.csproj",
"packagesPath": "C:\\Users\\muham\\.nuget\\packages\\",
"outputPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Application\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\muham\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {
"D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Domain\\MiniSkeletonAPI.Domain.csproj": {
"projectPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Domain\\MiniSkeletonAPI.Domain.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"dependencies": {
"AutoMapper": {
"target": "Package",
"version": "[13.0.1, )"
},
"FluentValidation.DependencyInjectionExtensions": {
"target": "Package",
"version": "[11.9.1, )"
},
"Microsoft.EntityFrameworkCore": {
"target": "Package",
"version": "[8.0.6, )"
},
"Microsoft.Extensions.Logging": {
"target": "Package",
"version": "[8.0.0, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.202/PortableRuntimeIdentifierGraph.json"
}
}
},
"D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Domain\\MiniSkeletonAPI.Domain.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Domain\\MiniSkeletonAPI.Domain.csproj",
"projectName": "MiniSkeletonAPI.Domain",
"projectPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Domain\\MiniSkeletonAPI.Domain.csproj",
"packagesPath": "C:\\Users\\muham\\.nuget\\packages\\",
"outputPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Domain\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\muham\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"dependencies": {
"MediatR": {
"target": "Package",
"version": "[12.2.0, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.202/PortableRuntimeIdentifierGraph.json"
}
}
},
"D:\\DevPT3\\MiniSkeletonAPI\\src\\MiniSkeletonAPI.Infrastructure\\MiniSkeletonAPI.Infrastructure.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "D:\\DevPT3\\MiniSkeletonAPI\\src\\MiniSkeletonAPI.Infrastructure\\MiniSkeletonAPI.Infrastructure.csproj",
"projectName": "MiniSkeletonAPI.Infrastructure",
"projectPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\MiniSkeletonAPI.Infrastructure\\MiniSkeletonAPI.Infrastructure.csproj",
"packagesPath": "C:\\Users\\muham\\.nuget\\packages\\",
"outputPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\MiniSkeletonAPI.Infrastructure\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages"
],
"configFilePaths": [
"C:\\Users\\muham\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net8.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"projectReferences": {
"D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Application\\MiniSkeletonAPI.Application.csproj": {
"projectPath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\core\\MiniSkeletonAPI.Application\\MiniSkeletonAPI.Application.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "direct"
}
},
"frameworks": {
"net8.0": {
"targetAlias": "net8.0",
"dependencies": {
"Microsoft.AspNetCore.Authentication.JwtBearer": {
"target": "Package",
"version": "[8.0.6, )"
},
"Microsoft.AspNetCore.Authorization": {
"target": "Package",
"version": "[8.0.5, )"
},
"Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": {
"target": "Package",
"version": "[8.0.5, )"
},
"Microsoft.AspNetCore.Identity.EntityFrameworkCore": {
"target": "Package",
"version": "[8.0.6, )"
},
"Microsoft.EntityFrameworkCore.Design": {
"include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive",
"suppressParent": "All",
"target": "Package",
"version": "[8.0.5, )"
},
"Microsoft.EntityFrameworkCore.Sqlite": {
"target": "Package",
"version": "[8.0.5, )"
},
"Microsoft.EntityFrameworkCore.Tools": {
"include": "Runtime, Build, Native, ContentFiles, Analyzers, BuildTransitive",
"suppressParent": "All",
"target": "Package",
"version": "[8.0.5, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\8.0.202/PortableRuntimeIdentifierGraph.json"
}
}
}
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\muham\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">6.9.1</NuGetToolVersion>
</PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="C:\Users\muham\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
</ItemGroup>
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)microsoft.entityframeworkcore\8.0.6\buildTransitive\net8.0\Microsoft.EntityFrameworkCore.props" Condition="Exists('$(NuGetPackageRoot)microsoft.entityframeworkcore\8.0.6\buildTransitive\net8.0\Microsoft.EntityFrameworkCore.props')" />
<Import Project="$(NuGetPackageRoot)microsoft.entityframeworkcore.design\8.0.5\build\net8.0\Microsoft.EntityFrameworkCore.Design.props" Condition="Exists('$(NuGetPackageRoot)microsoft.entityframeworkcore.design\8.0.5\build\net8.0\Microsoft.EntityFrameworkCore.Design.props')" />
</ImportGroup>
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<PkgMicrosoft_CodeAnalysis_Analyzers Condition=" '$(PkgMicrosoft_CodeAnalysis_Analyzers)' == '' ">C:\Users\muham\.nuget\packages\microsoft.codeanalysis.analyzers\3.3.3</PkgMicrosoft_CodeAnalysis_Analyzers>
<PkgMicrosoft_EntityFrameworkCore_Tools Condition=" '$(PkgMicrosoft_EntityFrameworkCore_Tools)' == '' ">C:\Users\muham\.nuget\packages\microsoft.entityframeworkcore.tools\8.0.5</PkgMicrosoft_EntityFrameworkCore_Tools>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)system.text.json\8.0.0\buildTransitive\net6.0\System.Text.Json.targets" Condition="Exists('$(NuGetPackageRoot)system.text.json\8.0.0\buildTransitive\net6.0\System.Text.Json.targets')" />
<Import Project="$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.6\buildTransitive\net8.0\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('$(NuGetPackageRoot)sqlitepclraw.lib.e_sqlite3\2.1.6\buildTransitive\net8.0\SQLitePCLRaw.lib.e_sqlite3.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.options\8.0.2\buildTransitive\net6.0\Microsoft.Extensions.Options.targets')" />
<Import Project="$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.1\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.extensions.logging.abstractions\8.0.1\buildTransitive\net6.0\Microsoft.Extensions.Logging.Abstractions.targets')" />
</ImportGroup>
</Project>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,77 @@
{
"version": 2,
"dgSpecHash": "7cWD2G2Fj+dIy96NiIYaAVV8Bk/T+solMMx+2nzCqMDZcsZ0pjOHPHqcPtDi9S4956MtQykyCcVgB5Z4MJlqxA==",
"success": true,
"projectFilePath": "D:\\DevPT3\\MiniSkeletonAPI\\src\\MiniSkeletonAPI.Infrastructure\\MiniSkeletonAPI.Infrastructure.csproj",
"expectedPackageFiles": [
"C:\\Users\\muham\\.nuget\\packages\\automapper\\13.0.1\\automapper.13.0.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\fluentvalidation\\11.9.1\\fluentvalidation.11.9.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\fluentvalidation.dependencyinjectionextensions\\11.9.1\\fluentvalidation.dependencyinjectionextensions.11.9.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\humanizer.core\\2.14.1\\humanizer.core.2.14.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\mediatr\\12.2.0\\mediatr.12.2.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\mediatr.contracts\\2.0.1\\mediatr.contracts.2.0.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.aspnetcore.authentication.jwtbearer\\8.0.6\\microsoft.aspnetcore.authentication.jwtbearer.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.aspnetcore.authorization\\8.0.5\\microsoft.aspnetcore.authorization.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.aspnetcore.cryptography.internal\\8.0.6\\microsoft.aspnetcore.cryptography.internal.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.aspnetcore.cryptography.keyderivation\\8.0.6\\microsoft.aspnetcore.cryptography.keyderivation.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.aspnetcore.diagnostics.entityframeworkcore\\8.0.5\\microsoft.aspnetcore.diagnostics.entityframeworkcore.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.aspnetcore.identity.entityframeworkcore\\8.0.6\\microsoft.aspnetcore.identity.entityframeworkcore.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.aspnetcore.metadata\\8.0.5\\microsoft.aspnetcore.metadata.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.bcl.asyncinterfaces\\6.0.0\\microsoft.bcl.asyncinterfaces.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.codeanalysis.analyzers\\3.3.3\\microsoft.codeanalysis.analyzers.3.3.3.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.codeanalysis.common\\4.5.0\\microsoft.codeanalysis.common.4.5.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.codeanalysis.csharp\\4.5.0\\microsoft.codeanalysis.csharp.4.5.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.codeanalysis.csharp.workspaces\\4.5.0\\microsoft.codeanalysis.csharp.workspaces.4.5.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.codeanalysis.workspaces.common\\4.5.0\\microsoft.codeanalysis.workspaces.common.4.5.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.data.sqlite.core\\8.0.5\\microsoft.data.sqlite.core.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore\\8.0.6\\microsoft.entityframeworkcore.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore.abstractions\\8.0.6\\microsoft.entityframeworkcore.abstractions.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore.analyzers\\8.0.6\\microsoft.entityframeworkcore.analyzers.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore.design\\8.0.5\\microsoft.entityframeworkcore.design.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore.relational\\8.0.6\\microsoft.entityframeworkcore.relational.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore.sqlite\\8.0.5\\microsoft.entityframeworkcore.sqlite.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore.sqlite.core\\8.0.5\\microsoft.entityframeworkcore.sqlite.core.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.entityframeworkcore.tools\\8.0.5\\microsoft.entityframeworkcore.tools.8.0.5.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.caching.abstractions\\8.0.0\\microsoft.extensions.caching.abstractions.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.caching.memory\\8.0.0\\microsoft.extensions.caching.memory.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.configuration.abstractions\\8.0.0\\microsoft.extensions.configuration.abstractions.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.dependencyinjection\\8.0.0\\microsoft.extensions.dependencyinjection.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.dependencyinjection.abstractions\\8.0.1\\microsoft.extensions.dependencyinjection.abstractions.8.0.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.dependencymodel\\8.0.0\\microsoft.extensions.dependencymodel.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.identity.core\\8.0.6\\microsoft.extensions.identity.core.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.identity.stores\\8.0.6\\microsoft.extensions.identity.stores.8.0.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.logging\\8.0.0\\microsoft.extensions.logging.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.logging.abstractions\\8.0.1\\microsoft.extensions.logging.abstractions.8.0.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.options\\8.0.2\\microsoft.extensions.options.8.0.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.extensions.primitives\\8.0.0\\microsoft.extensions.primitives.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.identitymodel.abstractions\\7.1.2\\microsoft.identitymodel.abstractions.7.1.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.identitymodel.jsonwebtokens\\7.1.2\\microsoft.identitymodel.jsonwebtokens.7.1.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.identitymodel.logging\\7.1.2\\microsoft.identitymodel.logging.7.1.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.identitymodel.protocols\\7.1.2\\microsoft.identitymodel.protocols.7.1.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.identitymodel.protocols.openidconnect\\7.1.2\\microsoft.identitymodel.protocols.openidconnect.7.1.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\microsoft.identitymodel.tokens\\7.1.2\\microsoft.identitymodel.tokens.7.1.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\mono.texttemplating\\2.2.1\\mono.texttemplating.2.2.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\sqlitepclraw.bundle_e_sqlite3\\2.1.6\\sqlitepclraw.bundle_e_sqlite3.2.1.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\sqlitepclraw.core\\2.1.6\\sqlitepclraw.core.2.1.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\sqlitepclraw.lib.e_sqlite3\\2.1.6\\sqlitepclraw.lib.e_sqlite3.2.1.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\sqlitepclraw.provider.e_sqlite3\\2.1.6\\sqlitepclraw.provider.e_sqlite3.2.1.6.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.codedom\\4.4.0\\system.codedom.4.4.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.collections.immutable\\6.0.0\\system.collections.immutable.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.composition\\6.0.0\\system.composition.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.composition.attributedmodel\\6.0.0\\system.composition.attributedmodel.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.composition.convention\\6.0.0\\system.composition.convention.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.composition.hosting\\6.0.0\\system.composition.hosting.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.composition.runtime\\6.0.0\\system.composition.runtime.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.composition.typedparts\\6.0.0\\system.composition.typedparts.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.identitymodel.tokens.jwt\\7.1.2\\system.identitymodel.tokens.jwt.7.1.2.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.io.pipelines\\6.0.3\\system.io.pipelines.6.0.3.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.memory\\4.5.3\\system.memory.4.5.3.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.reflection.metadata\\6.0.1\\system.reflection.metadata.6.0.1.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.runtime.compilerservices.unsafe\\6.0.0\\system.runtime.compilerservices.unsafe.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.text.encoding.codepages\\6.0.0\\system.text.encoding.codepages.6.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.text.encodings.web\\8.0.0\\system.text.encodings.web.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.text.json\\8.0.0\\system.text.json.8.0.0.nupkg.sha512",
"C:\\Users\\muham\\.nuget\\packages\\system.threading.channels\\6.0.0\\system.threading.channels.6.0.0.nupkg.sha512"
],
"logs": []
}

View File

@ -0,0 +1,20 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;
using MiniSkeletonAPI.Infrastructure.Data;
namespace MiniSkeletonAPI.Presentation.ContextFactory
{
public class ApplicationDbContextFactory : IDesignTimeDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext CreateDbContext(string[] args)
{
var configuration = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile("appsettings.json").Build();
var builder = new DbContextOptionsBuilder<ApplicationDbContext>()
.UseSqlite(configuration.GetConnectionString("DefaultConnection"),
b => b.MigrationsAssembly("MiniSkeletonAPI.Presentation"));
return new ApplicationDbContext(builder.Options);
}
}
}

View File

@ -0,0 +1,46 @@
using Microsoft.AspNetCore.Mvc;
using MiniSkeletonAPI.Application.Common.Interfaces;
using MiniSkeletonAPI.Infrastructure.Data;
using MiniSkeletonAPI.Presentation.Services;
using NSwag;
using NSwag.Generation.Processors.Security;
namespace MiniSkeletonAPI.Presentation;
public static class DependencyInjection
{
public static IServiceCollection AddWebServices(this IServiceCollection services)
{
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddScoped<IUser, CurrentUser>();
services.AddHttpContextAccessor();
services.AddHealthChecks()
.AddDbContextCheck<ApplicationDbContext>();
services.AddExceptionHandler<CustomExceptionHandler>();
// Customise default API behaviour
services.Configure<ApiBehaviorOptions>(options =>
options.SuppressModelStateInvalidFilter = true);
services.AddEndpointsApiExplorer();
services.AddOpenApiDocument((configure, sp) =>
{
configure.Title = "CleanArchitecture API";
configure.AddSecurity("JWT", Enumerable.Empty<string>(), new OpenApiSecurityScheme
{
Type = OpenApiSecuritySchemeType.ApiKey,
Name = "Authorization",
In = OpenApiSecurityApiKeyLocation.Header,
Description = "Type into the textbox: Bearer {your JWT token}."
});
configure.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("JWT"));
});
return services;
}
}

View File

@ -0,0 +1,6 @@
//namespace MiniSkeletonAPI.Presentation.Endpoints
//{
// public class Permissions
// {
// }
//}

View File

@ -0,0 +1,48 @@
using MiniSkeletonAPI.Infrastructure.Identity.Permission;
using Microsoft.AspNetCore.Authorization;
using MiniSkeletonAPI.Application.Identity.Roles.Commands.CreateRole;
using MiniSkeletonAPI.Application.Identity.Roles.Commands.UpdateRole;
using MiniSkeletonAPI.Application.Identity.Roles.Commands.DeleteRole;
using MiniSkeletonAPI.Application.Common.Models;
using MiniSkeletonAPI.Application.Identity.Roles.Queries.GetRolesWithPagination;
namespace MiniSkeletonAPI.Presentation.Endpoints;
public class Roles : EndpointGroupBase
{
public override void Map(WebApplication app)
{
app.MapGroup(this)
.MapGet(GetRolesWithPagination)
.MapPost(CreateRole)
.MapPut(UpdateRole, "{id}")
.MapDelete(DeleteRole, "{id}")
;
}
[Authorize(Permissions.Dashboards.View)]
public Task<PaginatedList<RoleBriefDto>> GetRolesWithPagination(ISender sender, [AsParameters] GetRolesWithPaginationQuery query)
{
return sender.Send(query);
}
[Authorize(Permissions.Dashboards.Create)]
public Task<Guid> CreateRole(ISender sender, CreateRoleCommand command)
{
return sender.Send(command);
}
[Authorize(Permissions.Dashboards.Create)]
public async Task<IResult> UpdateRole(ISender sender, Guid id, UpdateRoleCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
public async Task<IResult> DeleteRole(ISender sender, Guid id)
{
await sender.Send(new DeleteRoleCommand(id));
return Results.NoContent();
}
}

View File

@ -0,0 +1,54 @@
using Microsoft.AspNetCore.Authorization;
using MiniSkeletonAPI.Application.Common.Models;
using MiniSkeletonAPI.Application.TodoItems.Commands.CreateTodoItem;
using MiniSkeletonAPI.Application.TodoItems.Commands.DeleteTodoItem;
using MiniSkeletonAPI.Application.TodoItems.Commands.UpdateTodoItem;
using MiniSkeletonAPI.Application.TodoItems.Commands.UpdateTodoItemDetail;
using MiniSkeletonAPI.Application.TodoItems.Queries.GetTodoItemsWIthPagination;
using MiniSkeletonAPI.Infrastructure.Identity.Permission;
namespace MiniSkeletonAPI.Presentation.Endpoints;
public class TodoItems : EndpointGroupBase
{
public override void Map(WebApplication app)
{
app.MapGroup(this)
.MapGet(GetTodoItemsWithPagination)
.MapPost(CreateTodoItem)
.MapPut(UpdateTodoItem, "{id}")
.MapPut(UpdateTodoItemDetail, "UpdateDetail/{id}")
.MapDelete(DeleteTodoItem, "{id}");
}
[Authorize(Permissions.Dashboards.View)]
public Task<PaginatedList<TodoItemBriefDto>> GetTodoItemsWithPagination(ISender sender, [AsParameters] GetTodoItemsWithPaginationQuery query)
{
return sender.Send(query);
}
[Authorize(Permissions.Dashboards.Create)]
public Task<Guid> CreateTodoItem(ISender sender, CreateTodoItemCommand command)
{
return sender.Send(command);
}
public async Task<IResult> UpdateTodoItem(ISender sender, Guid id, UpdateTodoItemCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
public async Task<IResult> UpdateTodoItemDetail(ISender sender, Guid id, UpdateTodoItemDetailCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
public async Task<IResult> DeleteTodoItem(ISender sender, Guid id)
{
await sender.Send(new DeleteTodoItemCommand(id));
return Results.NoContent();
}
}

View File

@ -0,0 +1,43 @@

using MiniSkeletonAPI.Application.TodoLists.Commands.CreateTodoList;
using MiniSkeletonAPI.Application.TodoLists.Commands.DeleteTodoList;
using MiniSkeletonAPI.Application.TodoLists.Commands.UpdateTodoList;
using MiniSkeletonAPI.Application.TodoLists.Queries.GetTodos;
namespace MiniSkeletonAPI.Presentation.Endpoints;
public class TodoLists : EndpointGroupBase
{
public override void Map(WebApplication app)
{
app.MapGroup(this)
//.RequireAuthorization()
.MapGet(GetTodoLists)
.MapPost(CreateTodoList)
.MapPut(UpdateTodoList, "{id}")
.MapDelete(DeleteTodoList, "{id}");
}
public Task<TodosVm> GetTodoLists(ISender sender)
{
return sender.Send(new GetTodosQuery());
}
public Task<Guid> CreateTodoList(ISender sender, CreateTodoListCommand command)
{
return sender.Send(command);
}
public async Task<IResult> UpdateTodoList(ISender sender, Guid id, UpdateTodoListCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
public async Task<IResult> DeleteTodoList(ISender sender, Guid id)
{
await sender.Send(new DeleteTodoListCommand(id));
return Results.NoContent();
}
}

View File

@ -0,0 +1,49 @@

using Microsoft.AspNetCore.Authorization;
using MiniSkeletonAPI.Infrastructure.Identity;
using MiniSkeletonAPI.Infrastructure.Identity.Permission;
using MiniSkeletonAPI.Application.Common.Models;
using MiniSkeletonAPI.Application.Identity.Users.Commands.DeleteUser;
using MiniSkeletonAPI.Application.Identity.Users.Commands.CreateUser;
using MiniSkeletonAPI.Application.Identity.Users.Commands.UpdateUser;
using MiniSkeletonAPI.Application.Identity.Users.Queries.GetUsersWithPagination;
namespace CleanArchitecture.Web.Endpoints;
public class Users : EndpointGroupBase
{
public override void Map(WebApplication app)
{
app.MapGroup(this)
.MapGet(GetUsersWithPagination)
.MapPost(CreateUser)
.MapPut(UpdateUser, "{id}")
.MapDelete(DeleteUser, "{id}")
.MapCustomizedIdentityApi<ApplicationUser>();
}
[Authorize(Permissions.Dashboards.View)]
public Task<PaginatedList<UserBriefDto>> GetUsersWithPagination(ISender sender, [AsParameters] GetUsersWithPaginationQuery query)
{
return sender.Send(query);
}
[Authorize(Permissions.Dashboards.Create)]
public Task<Guid> CreateUser(ISender sender, CreateUserCommand command)
{
return sender.Send(command);
}
[Authorize(Permissions.Dashboards.Create)]
public async Task<IResult> UpdateUser(ISender sender, Guid id, UpdateUserCommand command)
{
if (id != command.Id) return Results.BadRequest();
await sender.Send(command);
return Results.NoContent();
}
public async Task<IResult> DeleteUser(ISender sender, Guid id)
{
await sender.Send(new DeleteUserCommand(id));
return Results.NoContent();
}
}

View File

@ -0,0 +1,2 @@
global using MiniSkeletonAPI.Presentation.Infrastructure;
global using MediatR;

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace MiniSkeletonAPI.Presentation.Helpers
{
public static class ClaimsHelper
{
public static void GetPermissions(this List<RoleClaimsDto> allPermissions, Type policy, string roleId)
{
FieldInfo[] fields = policy.GetFields(BindingFlags.Static | BindingFlags.Public);
foreach (FieldInfo fi in fields)
{
allPermissions.Add(new RoleClaimsDto { Value = fi.GetValue(null).ToString(), Type = "Permissions" });
}
}
public static async Task AddPermissionClaim(this RoleManager<IdentityRole> roleManager, IdentityRole role, string permission)
{
var allClaims = await roleManager.GetClaimsAsync(role);
if (!allClaims.Any(a => a.Type == "Permission" && a.Value == permission))
{
await roleManager.AddClaimAsync(role, new Claim("Permission", permission));
}
}
}
}

View File

@ -0,0 +1,163 @@
////using Entities.Exceptions;
//using Microsoft.AspNetCore.Mvc.Filters;
////using Newtonsoft.Json.Converters;
//using System;
//using System.Collections.Generic;
//using System.Globalization;
//using System.Linq;
//using System.Text;
//using System.Text.Json;
//using System.Text.Json.Serialization;
//using System.Threading.Tasks;
////using JsonSerializerNewtonsoft = Newtonsoft.Json.JsonSerializer;
////using JsonConverterNewtonsoft = Newtonsoft.Json;
//namespace MiniSkeletonAPI.Presentation.Helpers
//{
// public class DateTimeFormatConverter : JsonConverter<DateTime>
// {
// private readonly string format;
// public DateTimeFormatConverter(string format)
// {
// this.format = format;
// }
// public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
// {
// try
// {
// return DateTime.ParseExact(
// reader.GetString(),
// format,
// new CultureInfo("id-ID"));
// }
// catch (Exception ex)
// {
// throw new GeneralBadRquest(ex.Message);
// }
// }
// public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
// {
// ArgumentNullException.ThrowIfNull(writer, nameof(writer));
// writer.WriteStringValue(value
// //.ToUniversalTime()
// .ToString(
// format,
// CultureInfo.InvariantCulture));
// }
// }
// public class DateTimeConverterActionFilter : IActionFilter
// {
// public void OnActionExecuting(ActionExecutingContext context)
// {
// foreach (var argument in context.ActionArguments)
// {
// if (argument.Value is DateTime dateTime)
// {
// // Tambahkan 7 jam ke dalam tanggal
// DateTime convertedDate = dateTime.AddHours(7);
// context.ActionArguments[argument.Key] = convertedDate;
// }
// }
// }
// public void OnActionExecuted(ActionExecutedContext context)
// {
// // Kosongkan, tidak perlu diimplementasi untuk saat ini
// }
// }
// //public class NewtonSoftDateTimeFormatConverter : DateTimeConverterBase
// //{
// // private const string Format = "yyyy-MM-ddTHH:mm:ss.fffZ";
// // public NewtonSoftDateTimeFormatConverter()
// // {
// // Console.WriteLine(Format);
// // }
// // public override void WriteJson(JsonConverterNewtonsoft.JsonWriter writer, object value, JsonSerializerNewtonsoft serializer)
// // {
// // Console.WriteLine("MASUK NEWTON");
// // if (value is DateTime dateTime)
// // {
// // writer.WriteValue(dateTime.ToString(Format));
// // }
// // else
// // {
// // throw new JsonConverterNewtonsoft.JsonSerializationException("Expected DateTime object value.");
// // }
// // }
// // public override object ReadJson(JsonConverterNewtonsoft.JsonReader reader, Type objectType, object existingValue, JsonSerializerNewtonsoft serializer)
// // {
// // Console.WriteLine("KELUAR NEWTON");
// // if (reader.TokenType == JsonConverterNewtonsoft.JsonToken.String)
// // {
// // //if (DateTime.TryParseExact(reader.Value.ToString(), Format, null, System.Globalization.DateTimeStyles.None, out DateTime result))
// // //{
// // // return result;
// // //}
// // return DateTime.Parse(
// // reader.Value.ToString(),
// // new CultureInfo("id-ID"));
// // }
// // throw new JsonConverterNewtonsoft.JsonSerializationException("Unable to parse the DateTime value.");
// // }
// //}
// //public class NewtonSoftDateTimeFormatConverter : Newtonsoft.Json.JsonConverter<DateTime>
// //{
// // private readonly string format;
// // public NewtonSoftDateTimeFormatConverter(string format)
// // {
// // this.format = format;
// // }
// // public override DateTime ReadJson(ref JsonReader reader, Type typeToConvert, Newtonsoft.Json.JsonSerializer options)
// // {
// // Console.WriteLine("KENAPA GA KELUAR");
// // try
// // {
// // return DateTime.ParseExact(
// // reader.ReadAsString(),
// // this.format,
// // CultureInfo.InvariantCulture);
// // }
// // catch (Exception ex)
// // {
// // throw new GeneralBadRquest(ex.Message);
// // }
// // }
// // public override void WriteJson(JsonWriter writer, DateTime value, Newtonsoft.Json.JsonSerializer options)
// // {
// // Console.WriteLine("KENAPA GA 2");
// // ArgumentNullException.ThrowIfNull(writer, nameof(writer));
// // writer.Write(value
// // //.ToUniversalTime()
// // .ToString(
// // this.format,
// // CultureInfo.InvariantCulture));
// // }
// //}
// //public sealed class JsonDateTimeFormatAttribute : JsonConverterAttribute
// //{
// // private readonly string format;
// // public JsonDateTimeFormatAttribute(string format)
// // {
// // this.format = format;
// // }
// // public string Format => this.format;
// // public override JsonConverter? CreateConverter(Type typeToConvert)
// // {
// // return new DateTimeFormatConverter(this.format);
// // }
// //}
//}

View File

@ -0,0 +1,8 @@
namespace MiniSkeletonAPI.Presentation.Helpers;
public class RoleClaimsDto
{
public string Type { get; set; }
public string Value { get; set; }
public bool Selected { get; set; }
}

View File

@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace MiniSkeletonAPI.Presentation.Helpers
{
public class TimerManager
{
private Timer? _timer;
private AutoResetEvent? _autoResetEvent;
private Action? _action;
public DateTime TimerStarted { get; set; }
public bool IsTimerStarted { get; set; }
public void PrepareTimer(Action action)
{
_action = action;
_autoResetEvent = new AutoResetEvent(false);
_timer = new Timer(Execute, _autoResetEvent, 1000, 2000);
TimerStarted = DateTime.Now;
IsTimerStarted = true;
}
public void Execute(object? stateInfo)
{
_action();
if ((DateTime.Now - TimerStarted).TotalSeconds > 60)
{
IsTimerStarted = false;
_timer.Dispose();
}
}
}
}

View File

@ -0,0 +1,87 @@
using MiniSkeletonAPI.Application.Common.Exceptions;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
namespace MiniSkeletonAPI.Presentation.Infrastructure;
public class CustomExceptionHandler : IExceptionHandler
{
private readonly Dictionary<Type, Func<HttpContext, Exception, Task>> _exceptionHandlers;
public CustomExceptionHandler()
{
// Register known exception types and handlers.
_exceptionHandlers = new()
{
{ typeof(ValidationException), HandleValidationException },
//{ typeof(NotFoundException), HandleNotFoundException },
{ typeof(UnauthorizedAccessException), HandleUnauthorizedAccessException },
{ typeof(ForbiddenAccessException), HandleForbiddenAccessException },
};
}
public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
{
var exceptionType = exception.GetType();
if (_exceptionHandlers.ContainsKey(exceptionType))
{
await _exceptionHandlers[exceptionType].Invoke(httpContext, exception);
return true;
}
return false;
}
private async Task HandleValidationException(HttpContext httpContext, Exception ex)
{
var exception = (ValidationException)ex;
httpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
await httpContext.Response.WriteAsJsonAsync(new ValidationProblemDetails(exception.Errors)
{
Status = StatusCodes.Status400BadRequest,
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.1"
});
}
private async Task HandleNotFoundException(HttpContext httpContext, Exception ex)
{
//var exception = (NotFoundException)ex;
httpContext.Response.StatusCode = StatusCodes.Status404NotFound;
await httpContext.Response.WriteAsJsonAsync(new ProblemDetails()
{
Status = StatusCodes.Status404NotFound,
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.4",
Title = "The specified resource was not found.",
Detail = "Not found"
});
}
private async Task HandleUnauthorizedAccessException(HttpContext httpContext, Exception ex)
{
httpContext.Response.StatusCode = StatusCodes.Status401Unauthorized;
await httpContext.Response.WriteAsJsonAsync(new ProblemDetails
{
Status = StatusCodes.Status401Unauthorized,
Title = "Unauthorized",
Type = "https://tools.ietf.org/html/rfc7235#section-3.1"
});
}
private async Task HandleForbiddenAccessException(HttpContext httpContext, Exception ex)
{
httpContext.Response.StatusCode = StatusCodes.Status403Forbidden;
await httpContext.Response.WriteAsJsonAsync(new ProblemDetails
{
Status = StatusCodes.Status403Forbidden,
Title = "Forbidden",
Type = "https://tools.ietf.org/html/rfc7231#section-6.5.3"
});
}
}

View File

@ -0,0 +1,488 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Security.Claims;
using System.Text;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication.BearerToken;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.AspNetCore.WebUtilities;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
//using MiniSkeletonAPI.Application.Users.Requests;
using RegisterRequest = MiniSkeletonAPI.Application.Identity.Users.Requests.RegisterRequest;
using LoginRequest = MiniSkeletonAPI.Application.Identity.Users.Requests.LoginRequest;
using System.Text.Json;
namespace Microsoft.AspNetCore.Routing;
/// <summary>
/// Provides extension methods for <see cref="IEndpointRouteBuilder"/> to add identity endpoints.
/// </summary>
public static class CustomIdentityApiEndpointRouteBuilderExtensions
{
// Validate the email address using DataAnnotations like the UserValidator does when RequireUniqueEmail = true.
private static readonly EmailAddressAttribute _emailAddressAttribute = new();
/// <summary>
/// Add endpoints for registering, logging in, and logging out using ASP.NET Core Identity.
/// </summary>
/// <typeparam name="TUser">The type describing the user. This should match the generic parameter in <see cref="UserManager{TUser}"/>.</typeparam>
/// <param name="endpoints">
/// The <see cref="IEndpointRouteBuilder"/> to add the identity endpoints to.
/// Call <see cref="EndpointRouteBuilderExtensions.MapGroup(IEndpointRouteBuilder, string)"/> to add a prefix to all the endpoints.
/// </param>
/// <returns>An <see cref="IEndpointConventionBuilder"/> to further customize the added endpoints.</returns>
public static IEndpointConventionBuilder MapCustomizedIdentityApi<TUser>(this IEndpointRouteBuilder endpoints)
where TUser : class, new()
{
ArgumentNullException.ThrowIfNull(endpoints);
var timeProvider = endpoints.ServiceProvider.GetRequiredService<TimeProvider>();
var bearerTokenOptions = endpoints.ServiceProvider.GetRequiredService<IOptionsMonitor<BearerTokenOptions>>();
var emailSender = endpoints.ServiceProvider.GetRequiredService<IEmailSender<TUser>>();
var linkGenerator = endpoints.ServiceProvider.GetRequiredService<LinkGenerator>();
// We'll figure out a unique endpoint name based on the final route pattern during endpoint generation.
string? confirmEmailEndpointName = null;
var routeGroup = endpoints.MapGroup("");
// NOTE: We cannot inject UserManager<TUser> directly because the TUser generic parameter is currently unsupported by RDG.
// https://github.com/dotnet/aspnetcore/issues/47338
routeGroup.MapPost("/register", async Task<Results<Ok, ValidationProblem>>
([FromBody] RegisterRequest registration, HttpContext context, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
if (!userManager.SupportsUserEmail)
{
throw new NotSupportedException($"{nameof(MapCustomizedIdentityApi)} requires a user store with email support.");
}
var userStore = sp.GetRequiredService<IUserStore<TUser>>();
var emailStore = (IUserEmailStore<TUser>)userStore;
var email = registration.Email;
if (string.IsNullOrEmpty(email) || !_emailAddressAttribute.IsValid(email))
{
return CreateValidationProblem(IdentityResult.Failed(userManager.ErrorDescriber.InvalidEmail(email)));
}
var user = new TUser();
await userStore.SetUserNameAsync(user, email, CancellationToken.None);
await emailStore.SetEmailAsync(user, email, CancellationToken.None);
var result = await userManager.CreateAsync(user, registration.Password);
if (!result.Succeeded)
{
return CreateValidationProblem(result);
}
await SendConfirmationEmailAsync(user, userManager, context, email);
return TypedResults.Ok();
});
routeGroup.MapPost("/login", async Task<Results<Ok<AccessTokenResponse>, EmptyHttpResult, ProblemHttpResult>>
([FromBody] LoginRequest login, [FromQuery] bool? useCookies, [FromQuery] bool? useSessionCookies, [FromServices] IServiceProvider sp) =>
{
var signInManager = sp.GetRequiredService<SignInManager<TUser>>();
var useCookieScheme = (useCookies == true) || (useSessionCookies == true);
var isPersistent = (useCookies == true) && (useSessionCookies != true);
signInManager.AuthenticationScheme = useCookieScheme ? IdentityConstants.ApplicationScheme : IdentityConstants.BearerScheme;
var result = await signInManager.PasswordSignInAsync(login.Username, login.Password, isPersistent, lockoutOnFailure: true);
//Console.WriteLine(JsonSerializer.Serialize());
if (result.RequiresTwoFactor)
{
if (!string.IsNullOrEmpty(login.TwoFactorCode))
{
result = await signInManager.TwoFactorAuthenticatorSignInAsync(login.TwoFactorCode, isPersistent, rememberClient: isPersistent);
}
else if (!string.IsNullOrEmpty(login.TwoFactorRecoveryCode))
{
result = await signInManager.TwoFactorRecoveryCodeSignInAsync(login.TwoFactorRecoveryCode);
}
}
if (!result.Succeeded)
{
return TypedResults.Problem(result.ToString(), statusCode: StatusCodes.Status401Unauthorized);
}
// The signInManager already produced the needed response in the form of a cookie or bearer token.
return TypedResults.Empty;
});
routeGroup.MapPost("/refresh", async Task<Results<Ok<AccessTokenResponse>, UnauthorizedHttpResult, SignInHttpResult, ChallengeHttpResult>>
([FromBody] RefreshRequest refreshRequest, [FromServices] IServiceProvider sp) =>
{
var signInManager = sp.GetRequiredService<SignInManager<TUser>>();
var refreshTokenProtector = bearerTokenOptions.Get(IdentityConstants.BearerScheme).RefreshTokenProtector;
var refreshTicket = refreshTokenProtector.Unprotect(refreshRequest.RefreshToken);
// Reject the /refresh attempt with a 401 if the token expired or the security stamp validation fails
if (refreshTicket?.Properties?.ExpiresUtc is not { } expiresUtc ||
timeProvider.GetUtcNow() >= expiresUtc ||
await signInManager.ValidateSecurityStampAsync(refreshTicket.Principal) is not TUser user)
{
return TypedResults.Challenge();
}
var newPrincipal = await signInManager.CreateUserPrincipalAsync(user);
return TypedResults.SignIn(newPrincipal, authenticationScheme: IdentityConstants.BearerScheme);
});
routeGroup.MapGet("/confirmEmail", async Task<Results<ContentHttpResult, UnauthorizedHttpResult>>
([FromQuery] string userId, [FromQuery] string code, [FromQuery] string? changedEmail, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
if (await userManager.FindByIdAsync(userId) is not { } user)
{
// We could respond with a 404 instead of a 401 like Identity UI, but that feels like unnecessary information.
return TypedResults.Unauthorized();
}
try
{
code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(code));
}
catch (FormatException)
{
return TypedResults.Unauthorized();
}
IdentityResult result;
if (string.IsNullOrEmpty(changedEmail))
{
result = await userManager.ConfirmEmailAsync(user, code);
}
else
{
// As with Identity UI, email and user name are one and the same. So when we update the email,
// we need to update the user name.
result = await userManager.ChangeEmailAsync(user, changedEmail, code);
if (result.Succeeded)
{
result = await userManager.SetUserNameAsync(user, changedEmail);
}
}
if (!result.Succeeded)
{
return TypedResults.Unauthorized();
}
return TypedResults.Text("Thank you for confirming your email.");
})
.Add(endpointBuilder =>
{
var finalPattern = ((RouteEndpointBuilder)endpointBuilder).RoutePattern.RawText;
confirmEmailEndpointName = $"{nameof(MapCustomizedIdentityApi)}-{finalPattern}";
endpointBuilder.Metadata.Add(new EndpointNameMetadata(confirmEmailEndpointName));
});
routeGroup.MapPost("/resendConfirmationEmail", async Task<Ok>
([FromBody] ResendConfirmationEmailRequest resendRequest, HttpContext context, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
if (await userManager.FindByEmailAsync(resendRequest.Email) is not { } user)
{
return TypedResults.Ok();
}
await SendConfirmationEmailAsync(user, userManager, context, resendRequest.Email);
return TypedResults.Ok();
});
routeGroup.MapPost("/forgotPassword", async Task<Results<Ok, ValidationProblem>>
([FromBody] ForgotPasswordRequest resetRequest, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
var user = await userManager.FindByEmailAsync(resetRequest.Email);
if (user is not null && await userManager.IsEmailConfirmedAsync(user))
{
var code = await userManager.GeneratePasswordResetTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
await emailSender.SendPasswordResetCodeAsync(user, resetRequest.Email, HtmlEncoder.Default.Encode(code));
}
// Don't reveal that the user does not exist or is not confirmed, so don't return a 200 if we would have
// returned a 400 for an invalid code given a valid user email.
return TypedResults.Ok();
});
routeGroup.MapPost("/resetPassword", async Task<Results<Ok, ValidationProblem>>
([FromBody] ResetPasswordRequest resetRequest, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
var user = await userManager.FindByEmailAsync(resetRequest.Email);
if (user is null || !(await userManager.IsEmailConfirmedAsync(user)))
{
// Don't reveal that the user does not exist or is not confirmed, so don't return a 200 if we would have
// returned a 400 for an invalid code given a valid user email.
return CreateValidationProblem(IdentityResult.Failed(userManager.ErrorDescriber.InvalidToken()));
}
IdentityResult result;
try
{
var code = Encoding.UTF8.GetString(WebEncoders.Base64UrlDecode(resetRequest.ResetCode));
result = await userManager.ResetPasswordAsync(user, code, resetRequest.NewPassword);
}
catch (FormatException)
{
result = IdentityResult.Failed(userManager.ErrorDescriber.InvalidToken());
}
if (!result.Succeeded)
{
return CreateValidationProblem(result);
}
return TypedResults.Ok();
});
var accountGroup = routeGroup.MapGroup("/manage").RequireAuthorization();
accountGroup.MapPost("/2fa", async Task<Results<Ok<TwoFactorResponse>, ValidationProblem, NotFound>>
(ClaimsPrincipal claimsPrincipal, [FromBody] TwoFactorRequest tfaRequest, [FromServices] IServiceProvider sp) =>
{
var signInManager = sp.GetRequiredService<SignInManager<TUser>>();
var userManager = signInManager.UserManager;
if (await userManager.GetUserAsync(claimsPrincipal) is not { } user)
{
return TypedResults.NotFound();
}
if (tfaRequest.Enable == true)
{
if (tfaRequest.ResetSharedKey)
{
return CreateValidationProblem("CannotResetSharedKeyAndEnable",
"Resetting the 2fa shared key must disable 2fa until a 2fa token based on the new shared key is validated.");
}
else if (string.IsNullOrEmpty(tfaRequest.TwoFactorCode))
{
return CreateValidationProblem("RequiresTwoFactor",
"No 2fa token was provided by the request. A valid 2fa token is required to enable 2fa.");
}
else if (!await userManager.VerifyTwoFactorTokenAsync(user, userManager.Options.Tokens.AuthenticatorTokenProvider, tfaRequest.TwoFactorCode))
{
return CreateValidationProblem("InvalidTwoFactorCode",
"The 2fa token provided by the request was invalid. A valid 2fa token is required to enable 2fa.");
}
await userManager.SetTwoFactorEnabledAsync(user, true);
}
else if (tfaRequest.Enable == false || tfaRequest.ResetSharedKey)
{
await userManager.SetTwoFactorEnabledAsync(user, false);
}
if (tfaRequest.ResetSharedKey)
{
await userManager.ResetAuthenticatorKeyAsync(user);
}
string[]? recoveryCodes = null;
if (tfaRequest.ResetRecoveryCodes || (tfaRequest.Enable == true && await userManager.CountRecoveryCodesAsync(user) == 0))
{
var recoveryCodesEnumerable = await userManager.GenerateNewTwoFactorRecoveryCodesAsync(user, 10);
recoveryCodes = recoveryCodesEnumerable?.ToArray();
}
if (tfaRequest.ForgetMachine)
{
await signInManager.ForgetTwoFactorClientAsync();
}
var key = await userManager.GetAuthenticatorKeyAsync(user);
if (string.IsNullOrEmpty(key))
{
await userManager.ResetAuthenticatorKeyAsync(user);
key = await userManager.GetAuthenticatorKeyAsync(user);
if (string.IsNullOrEmpty(key))
{
throw new NotSupportedException("The user manager must produce an authenticator key after reset.");
}
}
return TypedResults.Ok(new TwoFactorResponse
{
SharedKey = key,
RecoveryCodes = recoveryCodes,
RecoveryCodesLeft = recoveryCodes?.Length ?? await userManager.CountRecoveryCodesAsync(user),
IsTwoFactorEnabled = await userManager.GetTwoFactorEnabledAsync(user),
IsMachineRemembered = await signInManager.IsTwoFactorClientRememberedAsync(user),
});
});
accountGroup.MapGet("/info", async Task<Results<Ok<InfoResponse>, ValidationProblem, NotFound>>
(ClaimsPrincipal claimsPrincipal, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
if (await userManager.GetUserAsync(claimsPrincipal) is not { } user)
{
return TypedResults.NotFound();
}
return TypedResults.Ok(await CreateInfoResponseAsync(user, userManager));
});
accountGroup.MapPost("/info", async Task<Results<Ok<InfoResponse>, ValidationProblem, NotFound>>
(ClaimsPrincipal claimsPrincipal, [FromBody] InfoRequest infoRequest, HttpContext context, [FromServices] IServiceProvider sp) =>
{
var userManager = sp.GetRequiredService<UserManager<TUser>>();
if (await userManager.GetUserAsync(claimsPrincipal) is not { } user)
{
return TypedResults.NotFound();
}
if (!string.IsNullOrEmpty(infoRequest.NewEmail) && !_emailAddressAttribute.IsValid(infoRequest.NewEmail))
{
return CreateValidationProblem(IdentityResult.Failed(userManager.ErrorDescriber.InvalidEmail(infoRequest.NewEmail)));
}
if (!string.IsNullOrEmpty(infoRequest.NewPassword))
{
if (string.IsNullOrEmpty(infoRequest.OldPassword))
{
return CreateValidationProblem("OldPasswordRequired",
"The old password is required to set a new password. If the old password is forgotten, use /resetPassword.");
}
var changePasswordResult = await userManager.ChangePasswordAsync(user, infoRequest.OldPassword, infoRequest.NewPassword);
if (!changePasswordResult.Succeeded)
{
return CreateValidationProblem(changePasswordResult);
}
}
if (!string.IsNullOrEmpty(infoRequest.NewEmail))
{
var email = await userManager.GetEmailAsync(user);
if (email != infoRequest.NewEmail)
{
await SendConfirmationEmailAsync(user, userManager, context, infoRequest.NewEmail, isChange: true);
}
}
return TypedResults.Ok(await CreateInfoResponseAsync(user, userManager));
});
async Task SendConfirmationEmailAsync(TUser user, UserManager<TUser> userManager, HttpContext context, string email, bool isChange = false)
{
if (confirmEmailEndpointName is null)
{
throw new NotSupportedException("No email confirmation endpoint was registered!");
}
var code = isChange
? await userManager.GenerateChangeEmailTokenAsync(user, email)
: await userManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var userId = await userManager.GetUserIdAsync(user);
var routeValues = new RouteValueDictionary()
{
["userId"] = userId,
["code"] = code,
};
if (isChange)
{
// This is validated by the /confirmEmail endpoint on change.
routeValues.Add("changedEmail", email);
}
var confirmEmailUrl = linkGenerator.GetUriByName(context, confirmEmailEndpointName, routeValues)
?? throw new NotSupportedException($"Could not find endpoint named '{confirmEmailEndpointName}'.");
await emailSender.SendConfirmationLinkAsync(user, email, HtmlEncoder.Default.Encode(confirmEmailUrl));
}
return new IdentityEndpointsConventionBuilder(routeGroup);
}
private static ValidationProblem CreateValidationProblem(string errorCode, string errorDescription) =>
TypedResults.ValidationProblem(new Dictionary<string, string[]> {
{ errorCode, [errorDescription] }
});
private static ValidationProblem CreateValidationProblem(IdentityResult result)
{
// We expect a single error code and description in the normal case.
// This could be golfed with GroupBy and ToDictionary, but perf! :P
Debug.Assert(!result.Succeeded);
var errorDictionary = new Dictionary<string, string[]>(1);
foreach (var error in result.Errors)
{
string[] newDescriptions;
if (errorDictionary.TryGetValue(error.Code, out var descriptions))
{
newDescriptions = new string[descriptions.Length + 1];
Array.Copy(descriptions, newDescriptions, descriptions.Length);
newDescriptions[descriptions.Length] = error.Description;
}
else
{
newDescriptions = [error.Description];
}
errorDictionary[error.Code] = newDescriptions;
}
return TypedResults.ValidationProblem(errorDictionary);
}
private static async Task<InfoResponse> CreateInfoResponseAsync<TUser>(TUser user, UserManager<TUser> userManager)
where TUser : class
{
return new()
{
Email = await userManager.GetEmailAsync(user) ?? throw new NotSupportedException("Users must have an email."),
IsEmailConfirmed = await userManager.IsEmailConfirmedAsync(user),
};
}
// Wrap RouteGroupBuilder with a non-public type to avoid a potential future behavioral breaking change.
private sealed class IdentityEndpointsConventionBuilder(RouteGroupBuilder inner) : IEndpointConventionBuilder
{
private IEndpointConventionBuilder InnerAsConventionBuilder => inner;
public void Add(Action<EndpointBuilder> convention) => InnerAsConventionBuilder.Add(convention);
public void Finally(Action<EndpointBuilder> finallyConvention) => InnerAsConventionBuilder.Finally(finallyConvention);
}
[AttributeUsage(AttributeTargets.Parameter)]
private sealed class FromBodyAttribute : Attribute, IFromBodyMetadata
{
}
[AttributeUsage(AttributeTargets.Parameter)]
private sealed class FromServicesAttribute : Attribute, IFromServiceMetadata
{
}
[AttributeUsage(AttributeTargets.Parameter)]
private sealed class FromQueryAttribute : Attribute, IFromQueryMetadata
{
public string? Name => null;
}
}

View File

@ -0,0 +1,6 @@
namespace MiniSkeletonAPI.Presentation.Infrastructure;
public abstract class EndpointGroupBase
{
public abstract void Map(WebApplication app);
}

View File

@ -0,0 +1,39 @@
using MiniSkeletonAPI.Presentation.Infrastructure;
using System.Diagnostics.CodeAnalysis;
namespace MiniSkeletonAPI.Presentation.Infrastructure;
public static class IEndpointRouteBuilderExtensions
{
public static IEndpointRouteBuilder MapGet(this IEndpointRouteBuilder builder, Delegate handler, [StringSyntax("Route")] string pattern = "")
{
builder.MapGet(pattern, handler)
.WithName(handler.Method.Name);
return builder;
}
public static IEndpointRouteBuilder MapPost(this IEndpointRouteBuilder builder, Delegate handler, [StringSyntax("Route")] string pattern = "")
{
builder.MapPost(pattern, handler)
.WithName(handler.Method.Name);
return builder;
}
public static IEndpointRouteBuilder MapPut(this IEndpointRouteBuilder builder, Delegate handler, [StringSyntax("Route")] string pattern)
{
builder.MapPut(pattern, handler)
.WithName(handler.Method.Name);
return builder;
}
public static IEndpointRouteBuilder MapDelete(this IEndpointRouteBuilder builder, Delegate handler, [StringSyntax("Route")] string pattern)
{
builder.MapDelete(pattern, handler)
.WithName(handler.Method.Name);
return builder;
}
}

View File

@ -0,0 +1,18 @@
using System.Reflection;
namespace MiniSkeletonAPI.Presentation.Infrastructure;
public static class MethodInfoExtensions
{
public static bool IsAnonymous(this MethodInfo method)
{
var invalidChars = new[] { '<', '>' };
return method.Name.Any(invalidChars.Contains);
}
//public static void AnonymousMethod(this IGuardClause guardClause, Delegate input)
//{
// if (input.Method.IsAnonymous())
// throw new ArgumentException("The endpoint name must be specified when using anonymous handlers.");
//}
}

View File

@ -0,0 +1,37 @@
using System.Reflection;
namespace MiniSkeletonAPI.Presentation.Infrastructure;
public static class WebApplicationExtensions
{
public static RouteGroupBuilder MapGroup(this WebApplication app, EndpointGroupBase group)
{
var groupName = group.GetType().Name;
return app
.MapGroup($"/api/{groupName}")
.WithGroupName(groupName)
.WithTags(groupName)
.WithOpenApi();
}
public static WebApplication MapEndpoints(this WebApplication app)
{
var endpointGroupType = typeof(EndpointGroupBase);
var assembly = Assembly.GetExecutingAssembly();
var endpointGroupTypes = assembly.GetExportedTypes()
.Where(t => t.IsSubclassOf(endpointGroupType));
foreach (var type in endpointGroupTypes)
{
if (Activator.CreateInstance(type) is EndpointGroupBase instance)
{
instance.Map(app);
}
}
return app;
}
}

Binary file not shown.

View File

@ -0,0 +1,383 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MiniSkeletonAPI.Infrastructure.Data;
#nullable disable
namespace MiniSkeletonAPI.Presentation.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
[Migration("20240529034332_Initialized")]
partial class Initialized
{
/// <inheritdoc />
protected override void BuildTargetModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Created")
.HasColumnType("TEXT");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<bool>("Done")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("TEXT");
b.Property<string>("LastModifiedBy")
.HasColumnType("TEXT");
b.Property<Guid>("ListId")
.HasColumnType("TEXT");
b.Property<string>("Note")
.HasColumnType("TEXT");
b.Property<int>("Priority")
.HasColumnType("INTEGER");
b.Property<DateTime?>("Reminder")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ListId");
b.ToTable("TodoItems");
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoList", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Created")
.HasColumnType("TEXT");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("TEXT");
b.Property<string>("LastModifiedBy")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("TodoLists");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<DateTime?>("LastCreated")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<DateTime>("Last_Created")
.HasColumnType("TEXT");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("RefreshToken")
.HasColumnType("TEXT");
b.Property<DateTime>("RefreshTokenExpiryTime")
.HasColumnType("TEXT");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUserRole", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoItem", b =>
{
b.HasOne("MiniSkeletonAPI.Domain.Entities.TodoList", "List")
.WithMany("Items")
.HasForeignKey("ListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("List");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUserRole", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", "Role")
.WithMany("UserRoles")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", "User")
.WithMany("UserRoles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoList", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", b =>
{
b.Navigation("UserRoles");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", b =>
{
b.Navigation("UserRoles");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,280 @@
using System;
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace MiniSkeletonAPI.Presentation.Migrations
{
/// <inheritdoc />
public partial class Initialized : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(
name: "AspNetRoles",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false),
LastCreated = table.Column<DateTime>(type: "TEXT", nullable: true),
Name = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoles", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetUsers",
columns: table => new
{
Id = table.Column<string>(type: "TEXT", nullable: false),
Last_Created = table.Column<DateTime>(type: "TEXT", nullable: false),
RefreshToken = table.Column<string>(type: "TEXT", nullable: true),
RefreshTokenExpiryTime = table.Column<DateTime>(type: "TEXT", nullable: false),
UserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedUserName = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
Email = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
NormalizedEmail = table.Column<string>(type: "TEXT", maxLength: 256, nullable: true),
EmailConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
PasswordHash = table.Column<string>(type: "TEXT", nullable: true),
SecurityStamp = table.Column<string>(type: "TEXT", nullable: true),
ConcurrencyStamp = table.Column<string>(type: "TEXT", nullable: true),
PhoneNumber = table.Column<string>(type: "TEXT", nullable: true),
PhoneNumberConfirmed = table.Column<bool>(type: "INTEGER", nullable: false),
TwoFactorEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
LockoutEnd = table.Column<DateTimeOffset>(type: "TEXT", nullable: true),
LockoutEnabled = table.Column<bool>(type: "INTEGER", nullable: false),
AccessFailedCount = table.Column<int>(type: "INTEGER", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUsers", x => x.Id);
});
migrationBuilder.CreateTable(
name: "TodoLists",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
Title = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
Created = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
CreatedBy = table.Column<string>(type: "TEXT", nullable: true),
LastModified = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
LastModifiedBy = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoLists", x => x.Id);
});
migrationBuilder.CreateTable(
name: "AspNetRoleClaims",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
RoleId = table.Column<string>(type: "TEXT", nullable: false),
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetRoleClaims_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserClaims",
columns: table => new
{
Id = table.Column<int>(type: "INTEGER", nullable: false)
.Annotation("Sqlite:Autoincrement", true),
UserId = table.Column<string>(type: "TEXT", nullable: false),
ClaimType = table.Column<string>(type: "TEXT", nullable: true),
ClaimValue = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserClaims", x => x.Id);
table.ForeignKey(
name: "FK_AspNetUserClaims_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserLogins",
columns: table => new
{
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
ProviderKey = table.Column<string>(type: "TEXT", nullable: false),
ProviderDisplayName = table.Column<string>(type: "TEXT", nullable: true),
UserId = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey });
table.ForeignKey(
name: "FK_AspNetUserLogins_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserRoles",
columns: table => new
{
UserId = table.Column<string>(type: "TEXT", nullable: false),
RoleId = table.Column<string>(type: "TEXT", nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId });
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetRoles_RoleId",
column: x => x.RoleId,
principalTable: "AspNetRoles",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
table.ForeignKey(
name: "FK_AspNetUserRoles_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "AspNetUserTokens",
columns: table => new
{
UserId = table.Column<string>(type: "TEXT", nullable: false),
LoginProvider = table.Column<string>(type: "TEXT", nullable: false),
Name = table.Column<string>(type: "TEXT", nullable: false),
Value = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name });
table.ForeignKey(
name: "FK_AspNetUserTokens_AspNetUsers_UserId",
column: x => x.UserId,
principalTable: "AspNetUsers",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "TodoItems",
columns: table => new
{
Id = table.Column<Guid>(type: "TEXT", nullable: false),
ListId = table.Column<Guid>(type: "TEXT", nullable: false),
Title = table.Column<string>(type: "TEXT", maxLength: 200, nullable: false),
Note = table.Column<string>(type: "TEXT", nullable: true),
Priority = table.Column<int>(type: "INTEGER", nullable: false),
Reminder = table.Column<DateTime>(type: "TEXT", nullable: true),
Done = table.Column<bool>(type: "INTEGER", nullable: false),
Created = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
CreatedBy = table.Column<string>(type: "TEXT", nullable: true),
LastModified = table.Column<DateTimeOffset>(type: "TEXT", nullable: false),
LastModifiedBy = table.Column<string>(type: "TEXT", nullable: true)
},
constraints: table =>
{
table.PrimaryKey("PK_TodoItems", x => x.Id);
table.ForeignKey(
name: "FK_TodoItems_TodoLists_ListId",
column: x => x.ListId,
principalTable: "TodoLists",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_AspNetRoleClaims_RoleId",
table: "AspNetRoleClaims",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "RoleNameIndex",
table: "AspNetRoles",
column: "NormalizedName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_AspNetUserClaims_UserId",
table: "AspNetUserClaims",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserLogins_UserId",
table: "AspNetUserLogins",
column: "UserId");
migrationBuilder.CreateIndex(
name: "IX_AspNetUserRoles_RoleId",
table: "AspNetUserRoles",
column: "RoleId");
migrationBuilder.CreateIndex(
name: "EmailIndex",
table: "AspNetUsers",
column: "NormalizedEmail");
migrationBuilder.CreateIndex(
name: "UserNameIndex",
table: "AspNetUsers",
column: "NormalizedUserName",
unique: true);
migrationBuilder.CreateIndex(
name: "IX_TodoItems_ListId",
table: "TodoItems",
column: "ListId");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropTable(
name: "AspNetRoleClaims");
migrationBuilder.DropTable(
name: "AspNetUserClaims");
migrationBuilder.DropTable(
name: "AspNetUserLogins");
migrationBuilder.DropTable(
name: "AspNetUserRoles");
migrationBuilder.DropTable(
name: "AspNetUserTokens");
migrationBuilder.DropTable(
name: "TodoItems");
migrationBuilder.DropTable(
name: "AspNetRoles");
migrationBuilder.DropTable(
name: "AspNetUsers");
migrationBuilder.DropTable(
name: "TodoLists");
}
}
}

View File

@ -0,0 +1,380 @@
// <auto-generated />
using System;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using MiniSkeletonAPI.Infrastructure.Data;
#nullable disable
namespace MiniSkeletonAPI.Presentation.Migrations
{
[DbContext(typeof(ApplicationDbContext))]
partial class ApplicationDbContextModelSnapshot : ModelSnapshot
{
protected override void BuildModel(ModelBuilder modelBuilder)
{
#pragma warning disable 612, 618
modelBuilder.HasAnnotation("ProductVersion", "8.0.6");
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("RoleId");
b.ToTable("AspNetRoleClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.Property<int>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER");
b.Property<string>("ClaimType")
.HasColumnType("TEXT");
b.Property<string>("ClaimValue")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("UserId");
b.ToTable("AspNetUserClaims", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("ProviderKey")
.HasColumnType("TEXT");
b.Property<string>("ProviderDisplayName")
.HasColumnType("TEXT");
b.Property<string>("UserId")
.IsRequired()
.HasColumnType("TEXT");
b.HasKey("LoginProvider", "ProviderKey");
b.HasIndex("UserId");
b.ToTable("AspNetUserLogins", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("LoginProvider")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasColumnType("TEXT");
b.Property<string>("Value")
.HasColumnType("TEXT");
b.HasKey("UserId", "LoginProvider", "Name");
b.ToTable("AspNetUserTokens", (string)null);
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoItem", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Created")
.HasColumnType("TEXT");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<bool>("Done")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("TEXT");
b.Property<string>("LastModifiedBy")
.HasColumnType("TEXT");
b.Property<Guid>("ListId")
.HasColumnType("TEXT");
b.Property<string>("Note")
.HasColumnType("TEXT");
b.Property<int>("Priority")
.HasColumnType("INTEGER");
b.Property<DateTime?>("Reminder")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("ListId");
b.ToTable("TodoItems");
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoList", b =>
{
b.Property<Guid>("Id")
.ValueGeneratedOnAdd()
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("Created")
.HasColumnType("TEXT");
b.Property<string>("CreatedBy")
.HasColumnType("TEXT");
b.Property<DateTimeOffset>("LastModified")
.HasColumnType("TEXT");
b.Property<string>("LastModifiedBy")
.HasColumnType("TEXT");
b.Property<string>("Title")
.IsRequired()
.HasMaxLength(200)
.HasColumnType("TEXT");
b.HasKey("Id");
b.ToTable("TodoLists");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<DateTime?>("LastCreated")
.HasColumnType("TEXT");
b.Property<string>("Name")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedName")
.IsUnique()
.HasDatabaseName("RoleNameIndex");
b.ToTable("AspNetRoles", (string)null);
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", b =>
{
b.Property<string>("Id")
.HasColumnType("TEXT");
b.Property<int>("AccessFailedCount")
.HasColumnType("INTEGER");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnType("TEXT");
b.Property<string>("Email")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<bool>("EmailConfirmed")
.HasColumnType("INTEGER");
b.Property<DateTime>("Last_Created")
.HasColumnType("TEXT");
b.Property<bool>("LockoutEnabled")
.HasColumnType("INTEGER");
b.Property<DateTimeOffset?>("LockoutEnd")
.HasColumnType("TEXT");
b.Property<string>("NormalizedEmail")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("NormalizedUserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.Property<string>("PasswordHash")
.HasColumnType("TEXT");
b.Property<string>("PhoneNumber")
.HasColumnType("TEXT");
b.Property<bool>("PhoneNumberConfirmed")
.HasColumnType("INTEGER");
b.Property<string>("RefreshToken")
.HasColumnType("TEXT");
b.Property<DateTime>("RefreshTokenExpiryTime")
.HasColumnType("TEXT");
b.Property<string>("SecurityStamp")
.HasColumnType("TEXT");
b.Property<bool>("TwoFactorEnabled")
.HasColumnType("INTEGER");
b.Property<string>("UserName")
.HasMaxLength(256)
.HasColumnType("TEXT");
b.HasKey("Id");
b.HasIndex("NormalizedEmail")
.HasDatabaseName("EmailIndex");
b.HasIndex("NormalizedUserName")
.IsUnique()
.HasDatabaseName("UserNameIndex");
b.ToTable("AspNetUsers", (string)null);
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUserRole", b =>
{
b.Property<string>("UserId")
.HasColumnType("TEXT");
b.Property<string>("RoleId")
.HasColumnType("TEXT");
b.HasKey("UserId", "RoleId");
b.HasIndex("RoleId");
b.ToTable("AspNetUserRoles", (string)null);
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", null)
.WithMany()
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken<string>", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", null)
.WithMany()
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoItem", b =>
{
b.HasOne("MiniSkeletonAPI.Domain.Entities.TodoList", "List")
.WithMany("Items")
.HasForeignKey("ListId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("List");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUserRole", b =>
{
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", "Role")
.WithMany("UserRoles")
.HasForeignKey("RoleId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.HasOne("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", "User")
.WithMany("UserRoles")
.HasForeignKey("UserId")
.OnDelete(DeleteBehavior.Cascade)
.IsRequired();
b.Navigation("Role");
b.Navigation("User");
});
modelBuilder.Entity("MiniSkeletonAPI.Domain.Entities.TodoList", b =>
{
b.Navigation("Items");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationRole", b =>
{
b.Navigation("UserRoles");
});
modelBuilder.Entity("MiniSkeletonAPI.Infrastructure.Identity.ApplicationUser", b =>
{
b.Navigation("UserRoles");
});
#pragma warning restore 612, 618
}
}
}

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.5" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.5">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="8.0.5" />
<PackageReference Include="NSwag.AspNetCore" Version="14.0.7" />
<PackageReference Include="NSwag.MSBuild" Version="14.0.7">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\core\MiniSkeletonAPI.Application\MiniSkeletonAPI.Application.csproj" />
<ProjectReference Include="..\MiniSkeletonAPI.Infrastructure\MiniSkeletonAPI.Infrastructure.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ActiveDebugProfile>https</ActiveDebugProfile>
</PropertyGroup>
</Project>

View File

@ -0,0 +1,58 @@
using Microsoft.AspNetCore.Authorization;
using MiniSkeletonAPI.Application;
using MiniSkeletonAPI.Infrastructure;
using MiniSkeletonAPI.Infrastructure.Data;
using MiniSkeletonAPI.Infrastructure.Identity;
using MiniSkeletonAPI.Infrastructure.Identity.Permission;
using MiniSkeletonAPI.Presentation;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddApplicationServices();
builder.Services.AddInfrastructureServices(builder.Configuration);
builder.Services.AddWebServices();
//builder.Services.AddAuthentication("Bearer")
// .AddJwtBearer(options =>
// {
// options.TokenValidationParameters = new TokenValidationParameters
// {
// ValidateIssuer = true,
// ValidateAudience = true,
// ValidateLifetime = true,
// ValidateIssuerSigningKey = true,
// ValidIssuer = builder.Configuration["Jwt:Issuer"],
// ValidAudience = builder.Configuration["Jwt:Audience"],
// IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
// };
// });
var app = builder.Build();
//app.MapCustomizedIdentityApi<ApplicationUser>();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
await app.InitialiseDatabaseAsync();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHealthChecks("/health");
app.UseHttpsRedirection();
app.UseAuthorization();
app.UseStaticFiles();
app.UseOpenApi();
app.UseSwaggerUi();
app.UseReDoc(options =>
{
options.Path = "/redoc";
});
app.MapFallbackToFile("index.html");
app.UseExceptionHandler(options => { });
//app.MapGet("/", () => "Hello World!");
app.MapEndpoints();
app.Run();
public partial class Program { }

View File

@ -0,0 +1,38 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:62118",
"sslPort": 44350
}
},
"profiles": {
"http": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"https": {
"commandName": "Project",
"dotnetRunMessages": true,
"launchBrowser": false,
"applicationUrl": "https://localhost:5001;http://localhost:5000",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": false,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
}
}

View File

@ -0,0 +1,16 @@
using System.Security.Claims;
using MiniSkeletonAPI.Application.Common.Interfaces;
namespace MiniSkeletonAPI.Presentation.Services;
public class CurrentUser : IUser
{
private readonly IHttpContextAccessor _httpContextAccessor;
public CurrentUser(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public string? Id => _httpContextAccessor.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier);
}

View File

@ -0,0 +1,6 @@
namespace MiniSkeletonAPI.Presentation.Settings;
public class JwtSettings
{
public string Secret { get; set; }
public int LifeTimeDays { get; set; }
}

View File

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

View File

@ -0,0 +1,23 @@
{
"ConnectionStrings": {
"DefaultConnection": "Data Source=LocalDatabase.db",
"Secret": "CodeMazeSecretKey"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"JwtSettings": {
"validIssuer": "MiniSkeletonAPI",
"validAudience": "https://localhost:5001"
}
//"Jwt": {
// "Key": "verySecretKeyWhichShouldBeLongAndSecure",
// "Issuer": "https://localhost:5001",
// "Audience": "https://localhost:5001"
//}
}

Some files were not shown because too many files have changed in this diff Show More