сделал авторизацию и регестрацию. Роли сломаны

This commit is contained in:
Vitaliy 2024-06-18 23:48:40 +03:00
parent f7f0a56c51
commit 304e7bf5f5
15 changed files with 359 additions and 86 deletions

4
.gitignore vendored
View File

@ -47,6 +47,8 @@ bld/
[Ll]og/ [Ll]og/
[Ll]ogs/ [Ll]ogs/
Migrations/
VolumeMount.AppHost-postgresql-data/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/
# Uncomment if you have tasks that create the project's static files in wwwroot # Uncomment if you have tasks that create the project's static files in wwwroot
@ -69,6 +71,8 @@ nunit-*.xml
[Rr]eleasePS/ [Rr]eleasePS/
dlldata.c dlldata.c
VolumeMount.AppHost-postgresql-data/
# Benchmark Results # Benchmark Results
BenchmarkDotNet.Artifacts/ BenchmarkDotNet.Artifacts/

View File

@ -0,0 +1,82 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Swashbuckle.AspNetCore.SwaggerGen;
public static class DbHelpersEndpointsExtensions
{
public static RouteGroupBuilder AddDbHelpersEndpoints(this WebApplication app)
{
var group = app.MapGroup("/db");
group.MapPost("/migrate/{contexClassName}", async (string contexClasstName, IServiceProvider services) =>
{
var contextType = typeof(Program).Assembly.GetType(contexClasstName);
if(contextType is null)
return Results.NotFound();
var inheritanceChain = contextType.GetInheritanceChain();
if(!inheritanceChain.Any(t => typeof(DbContext) == t))
return Results.NotFound();
using var scope = services.CreateScope();
var dbContext = (DbContext)scope.ServiceProvider.GetRequiredService(contextType);
await dbContext.Database.MigrateAsync();
return Results.Ok();
});
group.MapPost("/initdb/{contexClassName}", async (string contexClasstName, IServiceProvider services) =>
{
var contextType = typeof(Program).Assembly.GetType(contexClasstName);
if(contextType is null)
return Results.NotFound();
var inheritanceChain = contextType.GetInheritanceChain();
if(!inheritanceChain.Any(t => typeof(DbContext) == t))
return Results.NotFound();
using var scope = services.CreateScope();
var dbContext = (DbContext)scope.ServiceProvider.GetRequiredService(contextType);
await dbContext.Database.EnsureCreatedAsync();
return Results.Ok();
});
group.MapPost("/add-moderator", async ([FromServices]UserManager<User> userManager) => {
var user = new User{
Email = "m@gmail.com",
UserName = "m@gmail.com"
};
var result = await userManager.CreateAsync(user, "PassPass123_");
if(!result.Succeeded)
return Results.BadRequest(result.Errors);
var roleResult = await userManager.AddToRoleAsync(user, "moderator");
if(!roleResult.Succeeded)
return Results.BadRequest(roleResult.Errors);
return Results.Ok(new {
Login = "m@gmail.com",
Password = "PassPass123_",
Role = "moderator"
});
}).WithDescription($"Create user with login/email/username: m@gmail.com and password: PassPass123_");
group.WithTags("db");
return group;
}
}

View File

@ -0,0 +1,16 @@
using Microsoft.EntityFrameworkCore;
public static class ServicesExtensions
{
public static void AddDbContextWithDefaultConfiguration<TDbContext>(this WebApplicationBuilder builder) where TDbContext : DbContext
{
builder.AddNpgsqlDbContext<TDbContext>("prod", null,
optionsBuilder => optionsBuilder.UseNpgsql(npgsqlBuilder =>
npgsqlBuilder.MigrationsAssembly(typeof(Program).Assembly.GetName().Name)));
}
}

View File

@ -7,11 +7,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\HackathonPreparing.ServiceDefaults\HackathonPreparing.ServiceDefaults.csproj"/> <ProjectReference Include="..\HackathonPreparing.ServiceDefaults\HackathonPreparing.ServiceDefaults.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.1" /> <PackageReference Include="Aspire.Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.6" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -1,5 +1,5 @@
using HackathonPreparing.ApiService; using HackathonPreparing.ApiService;
using Microsoft.EntityFrameworkCore; using Microsoft.AspNetCore.Identity;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@ -7,90 +7,78 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c => builder.Services.AddSwaggerGen(c =>
{ {
c.SwaggerDoc("v1", new OpenApiInfo { c.SchemaFilter<UserOpenApiSchemeFilter>();
Title = "Weather Forecast API", c.AddSecurityDefinition("AccessToken", new OpenApiSecurityScheme {
Description = "Weather forecast API for the HackathonPreparing project.", In = ParameterLocation.Header,
Version = "v1" }); Description = "can get in /login or /register",
Name = "Authorization",
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
Scheme = "bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme {
Reference = new OpenApiReference{
Type = ReferenceType.SecurityScheme,
Id = "AccessToken"
}
},
new string[]{}
}
});
}); });
// Add service defaults & Aspire components. // Add service defaults & Aspire components.
builder.AddServiceDefaults(); //we dont need it now
//builder.AddServiceDefaults();
builder.Services.AddAuthorization();
builder.Services.AddAuthorizationBuilder()
.AddPolicy("moderator", policy =>
policy.RequireRole("MODERATOR"));
//TODO BEARER
builder.Services.AddAuthentication().AddBearerToken(IdentityConstants.BearerScheme);
builder.Services.AddIdentity<User, IdentityRole<int>>()
.AddEntityFrameworkStores<UserContext>()
.AddApiEndpoints();
// Add services to the container. // Add services to the container.
builder.Services.AddProblemDetails(); builder.Services.AddProblemDetails();
builder.AddNpgsqlDbContext<DatabaseContext>("prod", null, builder.AddDbContextWithDefaultConfiguration<WeatherForecastContext>();
optionsBuilder => optionsBuilder.UseNpgsql(npgsqlBuilder => builder.AddDbContextWithDefaultConfiguration<UserContext>();
npgsqlBuilder.MigrationsAssembly(typeof(Program).Assembly.GetName().Name)));
var app = builder.Build(); var app = builder.Build();
var logger = app.Services.GetRequiredService<ILogger<Program>>(); app.UsePathBase(new PathString("/api"));
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
var logger = app.Services.GetRequiredService<ILogger<Program>>();
logger.LogWarning("!!!!!!! Call /ensure-created enpoint or /migrate if no db !!!!!!!!"); logger.LogWarning("!!!!!!! Call /ensure-created enpoint or /migrate if no db !!!!!!!!");
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(c => app.UseSwaggerUI();
{ app.AddDbHelpersEndpoints();
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Weather Forecast API V1");
});
app.MapGet("/ensure-created", async (DatabaseContext db) => await db.Database.EnsureCreatedAsync());
app.MapGet("/migrate", async (DatabaseContext db) => await db.Database.MigrateAsync());
} }
// Configure the HTTP request pipeline. // Configure the HTTP request pipeline.
app.UseExceptionHandler(); app.UseExceptionHandler();
app.UseAuthorization();
app.UseAuthentication();
app.MapGet("/weatherforecast", async (DatabaseContext db) => await db.Forecasts.ToListAsync()); app.AddWeatherForecastEndpoints();
app.MapDelete("/weatherforecast/{id}", async (DatabaseContext db, int id) =>
{
var forecast = await db.Forecasts.FindAsync(id);
if (forecast == null)
{
return Results.NotFound();
}
db.Forecasts.Remove(forecast);
await db.SaveChangesAsync();
return Results.NoContent();
});
app.MapPost("/weatherforecast", async (DatabaseContext db, WeatherForecast forecast) =>
{
db.Forecasts.Add(forecast);
await db.SaveChangesAsync();
return Results.Created($"/weatherforecast/{forecast.Id}", forecast);
});
app.MapPut("/weatherforecast/{id}", async (DatabaseContext db, int id, WeatherForecast forecast) =>
{
if (id != forecast.Id)
{
return Results.BadRequest();
}
db.Entry(forecast).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (await db.Forecasts.FindAsync(id) == null)
{
return Results.NotFound();
}
throw;
}
return Results.NoContent();
});
app.MapDefaultEndpoints(); app.MapDefaultEndpoints();
app.MapGroup("/user")
.MapIdentityApi<User>()
.WithTags("user");
app.Run(); app.Run();

View File

@ -0,0 +1,20 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
public class IdentityRoleEntityTypeConfiguration : IEntityTypeConfiguration<IdentityRole<int>>
{
public void Configure(EntityTypeBuilder<IdentityRole<int>> builder)
{
builder.HasData(
[
new IdentityRole<int>{
Id = 1,
Name = "moderator",
NormalizedName = "MODERATOR",
ConcurrencyStamp = Guid.NewGuid().ToString()
}
]
);
}
}

View File

@ -0,0 +1,12 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
//Example how add Fluent Building in project
public class UserEntityTypeConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
}
}

View File

@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Identity;
public class User : IdentityUser<int>
{
public string? AboutMe { get; set; }
public override bool TwoFactorEnabled => false;
}

View File

@ -0,0 +1,19 @@
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;
public class UserContext : IdentityDbContext<User, IdentityRole<int>, int>
{
public UserContext (DbContextOptions<UserContext> options) : base(options) {}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.ApplyConfigurationsFromAssembly(typeof(Program).Assembly);
builder.HasDefaultSchema("user");
}
}

View File

@ -0,0 +1,33 @@
using HackathonPreparing.ApiService.Migrations;
using Microsoft.AspNetCore.Identity.Data;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public class UserOpenApiSchemeFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (context.Type == typeof(LoginRequest))
{
schema.Example = new OpenApiObject()
{
["email"] = new OpenApiString("email@gmail.com"),
["password"] = new OpenApiString("PassPass123_"),
};
}
if (context.Type == typeof(RegisterRequest))
{
schema.Example = new OpenApiObject()
{
["email"] = new OpenApiString("email@gmail.com"),
["password"] = new OpenApiString("PassPass123_"),
};
}
}
}

View File

@ -0,0 +1,9 @@
using Microsoft.AspNetCore.Mvc;
namespace HackathonPreparing.ApiService.User
{
public class UserController : Controller
{
}
}

View File

@ -0,0 +1,18 @@
namespace HackathonPreparing.ApiService;
public class WeatherForecast
{
public WeatherForecast(int id, DateOnly date, int temperatureC, string summary)
{
Id = id;
Date = date;
TemperatureC = temperatureC;
Summary = summary;
}
public int Id { get; set; }
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
}

View File

@ -2,9 +2,9 @@ using Microsoft.EntityFrameworkCore;
namespace HackathonPreparing.ApiService; namespace HackathonPreparing.ApiService;
public sealed class DatabaseContext : DbContext public sealed class WeatherForecastContext : DbContext
{ {
public DatabaseContext(DbContextOptions<DatabaseContext> options) public WeatherForecastContext(DbContextOptions<WeatherForecastContext> options)
: base(options) : base(options)
{ {
@ -39,20 +39,3 @@ public sealed class DatabaseContext : DbContext
base.OnModelCreating(modelBuilder); base.OnModelCreating(modelBuilder);
} }
} }
public class WeatherForecast
{
public WeatherForecast(int id, DateOnly date, int temperatureC, string summary)
{
Id = id;
Date = date;
TemperatureC = temperatureC;
Summary = summary;
}
public int Id { get; set; }
public DateOnly Date { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
}

View File

@ -0,0 +1,60 @@
using HackathonPreparing.ApiService;
using Microsoft.EntityFrameworkCore;
public static class WeatherForecastExtensions
{
public static void AddWeatherForecastEndpoints (this WebApplication app)
{
var group = app.MapGroup("/weatherforecast");
group.MapGet("/", async (WeatherForecastContext db) => await db.Forecasts.ToListAsync());
group.MapDelete("/{id}", async (WeatherForecastContext db, int id) =>
{
var forecast = await db.Forecasts.FindAsync(id);
if (forecast == null)
{
return Results.NotFound();
}
db.Forecasts.Remove(forecast);
await db.SaveChangesAsync();
return Results.NoContent();
}).RequireAuthorization("moderator");
group.MapPost("/", async (WeatherForecastContext db, WeatherForecast forecast) =>
{
db.Forecasts.Add(forecast);
await db.SaveChangesAsync();
return Results.Created($"/weatherforecast/{forecast.Id}", forecast);
});
group.MapPut("/{id}", async (WeatherForecastContext db, int id, WeatherForecast forecast) =>
{
if (id != forecast.Id)
{
return Results.BadRequest();
}
db.Entry(forecast).State = EntityState.Modified;
try
{
await db.SaveChangesAsync();
}
catch (DbUpdateConcurrencyException)
{
if (await db.Forecasts.FindAsync(id) == null)
{
return Results.NotFound();
}
throw;
}
return Results.NoContent();
}).RequireAuthorization("moderator");
group.WithTags("weatherforecast");
}
}

View File

@ -1,9 +1,26 @@
var builder = DistributedApplication.CreateBuilder(args); var builder = DistributedApplication.CreateBuilder(args);
//use it
//dotnet user-secrets set Parameters:postgresql-password _StroNG_PaSSworD_
//dotnet user-secrets set Parameters:postgresql-username dev_user
//.WithBindMount("VolumeMount.AppHost-postgresql-data", "/var/lib/postgresql/data")
var cache = builder.AddRedis("cache"); var cache = builder.AddRedis("cache");
var postgres = builder.AddPostgres("postgres").WithPgAdmin(); var sqlPassword = builder.AddParameter("postgresql-password", secret: true);
var postgresdb = postgres.AddDatabase("prod");
var sqlUser = builder.AddParameter("postgresql-username", secret: true);
var postgresdb = builder
//.AddPostgres("postgres", password: sqlPassword, userName: sqlUser)
.AddPostgres("postgres")
.WithPgAdmin()
//.WithDataVolume()
.AddDatabase("prod");
var apiService = builder.AddProject<Projects.HackathonPreparing_ApiService>("apiservice").WithReference(postgresdb); var apiService = builder.AddProject<Projects.HackathonPreparing_ApiService>("apiservice").WithReference(postgresdb);
builder.AddProject<Projects.HackathonPreparing_Web>("webfrontend") builder.AddProject<Projects.HackathonPreparing_Web>("webfrontend")