first commit
This commit is contained in:
commit
7a653f6d34
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
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.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
|
@ -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
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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>();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -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>
|
Binary file not shown.
Binary file not shown.
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.
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
// <autogenerated />
|
||||
using System;
|
||||
using System.Reflection;
|
||||
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v8.0", FrameworkDisplayName = ".NET 8.0")]
|
|
@ -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.
|
||||
|
|
@ -0,0 +1 @@
|
|||
955e66488cb04eb98005da0fb6c4021b5e7ca317d6e0f2985650912d716e46aa
|
|
@ -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 =
|
|
@ -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;
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
31ec96ef62c2578f82b3ffb599b310d4a2b9a9ca98a54ccedcec732bf6534c94
|
|
@ -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
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
a1f0c67468e8d76250a2a3fc99ee2180f5bfabc358e32d63fdd7e9aaaec14440
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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
|
@ -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": []
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
//namespace MiniSkeletonAPI.Presentation.Endpoints
|
||||
//{
|
||||
// public class Permissions
|
||||
// {
|
||||
// }
|
||||
//}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
global using MiniSkeletonAPI.Presentation.Infrastructure;
|
||||
global using MediatR;
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
// // }
|
||||
// //}
|
||||
//}
|
|
@ -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; }
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace MiniSkeletonAPI.Presentation.Infrastructure;
|
||||
|
||||
public abstract class EndpointGroupBase
|
||||
{
|
||||
public abstract void Map(WebApplication app);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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.");
|
||||
//}
|
||||
}
|
|
@ -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.
383
src/MiniSkeletonAPI.Presentation/Migrations/20240529034332_Initialized.Designer.cs
generated
Normal file
383
src/MiniSkeletonAPI.Presentation/Migrations/20240529034332_Initialized.Designer.cs
generated
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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>
|
|
@ -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 { }
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
namespace MiniSkeletonAPI.Presentation.Settings;
|
||||
public class JwtSettings
|
||||
{
|
||||
public string Secret { get; set; }
|
||||
public int LifeTimeDays { get; set; }
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
//}
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue