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

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]ogs/
Migrations/
VolumeMount.AppHost-postgresql-data/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
@ -69,6 +71,8 @@ nunit-*.xml
[Rr]eleasePS/
dlldata.c
VolumeMount.AppHost-postgresql-data/
# Benchmark Results
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

@ -12,6 +12,8 @@
<ItemGroup>
<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">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>

View File

@ -1,5 +1,5 @@
using HackathonPreparing.ApiService;
using Microsoft.EntityFrameworkCore;
using Microsoft.AspNetCore.Identity;
using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args);
@ -7,90 +7,78 @@ var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Weather Forecast API",
Description = "Weather forecast API for the HackathonPreparing project.",
Version = "v1" });
c.SchemaFilter<UserOpenApiSchemeFilter>();
c.AddSecurityDefinition("AccessToken", new OpenApiSecurityScheme {
In = ParameterLocation.Header,
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.
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.
builder.Services.AddProblemDetails();
builder.AddNpgsqlDbContext<DatabaseContext>("prod", null,
optionsBuilder => optionsBuilder.UseNpgsql(npgsqlBuilder =>
npgsqlBuilder.MigrationsAssembly(typeof(Program).Assembly.GetName().Name)));
builder.AddDbContextWithDefaultConfiguration<WeatherForecastContext>();
builder.AddDbContextWithDefaultConfiguration<UserContext>();
var app = builder.Build();
var logger = app.Services.GetRequiredService<ILogger<Program>>();
app.UsePathBase(new PathString("/api"));
if (app.Environment.IsDevelopment())
{
var logger = app.Services.GetRequiredService<ILogger<Program>>();
logger.LogWarning("!!!!!!! Call /ensure-created enpoint or /migrate if no db !!!!!!!!");
app.UseSwagger();
app.UseSwaggerUI(c =>
{
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());
app.UseSwaggerUI();
app.AddDbHelpersEndpoints();
}
// Configure the HTTP request pipeline.
app.UseExceptionHandler();
app.UseAuthorization();
app.UseAuthentication();
app.MapGet("/weatherforecast", async (DatabaseContext db) => await db.Forecasts.ToListAsync());
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.AddWeatherForecastEndpoints();
app.MapDefaultEndpoints();
app.MapGroup("/user")
.MapIdentityApi<User>()
.WithTags("user");
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;
public sealed class DatabaseContext : DbContext
public sealed class WeatherForecastContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options)
public WeatherForecastContext(DbContextOptions<WeatherForecastContext> options)
: base(options)
{
@ -39,20 +39,3 @@ public sealed class DatabaseContext : DbContext
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);
//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 postgres = builder.AddPostgres("postgres").WithPgAdmin();
var postgresdb = postgres.AddDatabase("prod");
var sqlPassword = builder.AddParameter("postgresql-password", secret: true);
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);
builder.AddProject<Projects.HackathonPreparing_Web>("webfrontend")