db works, upload works

This commit is contained in:
Alex Bezdieniezhnykh
2024-11-12 22:16:50 +02:00
parent 2336c15aa4
commit ddbf8114ba
15 changed files with 66 additions and 63 deletions
+2
View File
@@ -6,3 +6,5 @@ obj
*.user *.user
log* log*
*.cmd *.cmd
*permissions.sql
Content/
+14 -7
View File
@@ -7,10 +7,12 @@ using Azaion.Common.Requests;
using Azaion.Services; using Azaion.Services;
using FluentValidation; using FluentValidation;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(o => o.Limits.MaxRequestBodySize = 209715200); //increase upload limit up to 200mb
var jwtConfig = builder.Configuration.GetSection(nameof(JwtConfig)).Get<JwtConfig>(); var jwtConfig = builder.Configuration.GetSection(nameof(JwtConfig)).Get<JwtConfig>();
if (jwtConfig == null) if (jwtConfig == null)
@@ -31,7 +33,10 @@ builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
IssuerSigningKey = signingKey IssuerSigningKey = signingKey
}; };
}); });
builder.Services.AddAuthorization();
var apiAdminPolicy = new AuthorizationPolicyBuilder().RequireRole(RoleEnum.ApiAdmin.ToString()).Build();
builder.Services.AddAuthorization(o => o.AddPolicy("apiAdminPolicy", apiAdminPolicy));
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
@@ -87,21 +92,23 @@ app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.MapPost("/login", app.MapPost("/login",
async (string username, string password, IUserService userService, IAuthService authService, CancellationToken cancellationToken) => async (LoginRequest request, IUserService userService, IAuthService authService, CancellationToken cancellationToken) =>
{ {
var user = await userService.ValidateUser(username, password, cancellationToken: cancellationToken); var user = await userService.ValidateUser(request, cancellationToken: cancellationToken);
return Results.Ok(new { Token = authService.CreateToken(user)}); return Results.Ok(new { Token = authService.CreateToken(user)});
}); });
app.MapPost("/register-user", app.MapPost("/register-user",
async (RegisterUserRequest registerUserRequest, IUserService userService, CancellationToken cancellationToken) async (RegisterUserRequest registerUserRequest, IUserService userService, CancellationToken cancellationToken)
=> await userService.RegisterUser(registerUserRequest, cancellationToken)) => await userService.RegisterUser(registerUserRequest, cancellationToken))
.RequireAuthorization(p => p.RequireRole(RoleEnum.ApiAdmin.ToString())); .RequireAuthorization(apiAdminPolicy);
app.MapPost("/resources", app.MapPost("/resources",
async (UploadResourceRequest uploadResourceRequest, IResourcesService resourceService, CancellationToken cancellationToken) async (ResourceEnum resourceEnum, IFormFile data, IResourcesService resourceService, CancellationToken cancellationToken)
=> await resourceService.SaveResource(uploadResourceRequest, cancellationToken)) => await resourceService.SaveResource(resourceEnum, data, cancellationToken))
.RequireAuthorization(p => p.RequireRole(RoleEnum.ApiAdmin.ToString())); .Accepts<IFormFile>("multipart/form-data")
.RequireAuthorization(apiAdminPolicy)
.DisableAntiforgery();
app.MapPost("/resources/get", app.MapPost("/resources/get",
async (GetResourceRequest request, IAuthService authService, IUserService userService, IResourcesService resourcesService, CancellationToken cancellationToken) => async (GetResourceRequest request, IAuthService authService, IUserService userService, IResourcesService resourcesService, CancellationToken cancellationToken) =>
+1
View File
@@ -9,6 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="FluentValidation" Version="11.10.0" /> <PackageReference Include="FluentValidation" Version="11.10.0" />
<PackageReference Include="linq2db" Version="5.4.1" /> <PackageReference Include="linq2db" Version="5.4.1" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Npgsql" Version="8.0.5" /> <PackageReference Include="Npgsql" Version="8.0.5" />
</ItemGroup> </ItemGroup>
+2 -1
View File
@@ -15,5 +15,6 @@ public enum ExceptionEnum
WrongEmail = 35, WrongEmail = 35,
PasswordLengthIncorrect = 37, PasswordLengthIncorrect = 37,
HardwareIdMismatch = 40, HardwareIdMismatch = 40,
WrongResourceType = 50 WrongResourceType = 50,
NoFile = 60
} }
@@ -1,4 +1,5 @@
using Azaion.Common.Entities; using Azaion.Common.Entities;
using LinqToDB;
using LinqToDB.Mapping; using LinqToDB.Mapping;
namespace Azaion.Common.Database; namespace Azaion.Common.Database;
@@ -21,8 +22,12 @@ public static class AzaionDbSchemaHolder
builder.Entity<User>() builder.Entity<User>()
.HasTableName("users") .HasTableName("users")
.HasIdentity(x => x.Id) .Property(x => x.Id)
.Property(x => x.Role).HasConversion(v => v.ToString(), v => (RoleEnum)Enum.Parse(typeof(RoleEnum), v)); .IsPrimaryKey()
.HasDataType(DataType.Guid)
.Property(x => x.Role)
.HasDataType(DataType.Text)
.HasConversion(v => v.ToString(), v => (RoleEnum)Enum.Parse(typeof(RoleEnum), v));
builder.Build(); builder.Build();
} }
+1 -1
View File
@@ -4,5 +4,5 @@ public enum ResourceEnum
{ {
AnnotatorDll = 10, AnnotatorDll = 10,
AIModelRKNN = 20, AIModelRKNN = 20,
AIModelONNX = 20, AIModelONNX = 30,
} }
+6 -5
View File
@@ -2,9 +2,10 @@
public enum RoleEnum public enum RoleEnum
{ {
Operator, None = 0,
Validator, Operator = 10,
CompanionPC, Validator = 20,
Admin, CompanionPC = 30,
ApiAdmin Admin = 40,
ApiAdmin = 1000
} }
+1 -1
View File
@@ -3,7 +3,7 @@
public class User public class User
{ {
public Guid Id { get; set; } public Guid Id { get; set; }
public string Email { get; set; } = null!; public string Email { get; set; } = null!;
public string PasswordHash { get; set; } = null!; public string PasswordHash { get; set; } = null!;
public string HardwareId { get; set; } = null!; public string HardwareId { get; set; } = null!;
public RoleEnum Role { get; set; } public RoleEnum Role { get; set; }
@@ -8,9 +8,3 @@ public class GetResourceRequest
public string HardwareId { get; set; } = null!; public string HardwareId { get; set; } = null!;
public ResourceEnum ResourceEnum { get; set; } public ResourceEnum ResourceEnum { get; set; }
} }
public class UploadResourceRequest
{
public ResourceEnum ResourceEnum { get; set; }
public Stream Data { get; set; } = null!;
}
+1 -1
View File
@@ -49,7 +49,7 @@ public class AuthService(IHttpContextAccessor httpContextAccessor, IOptions<JwtC
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()), new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
new Claim(ClaimTypes.Name, user.Email), new Claim(ClaimTypes.Name, user.Email),
new Claim(ClaimTypes.Role, user.Role.ToString()), new Claim(ClaimTypes.Role, user.Role.ToString()),
new Claim(Constants.HARDWARE_ID, user.HardwareId) new Claim(Constants.HARDWARE_ID, user.HardwareId ?? "")
]), ]),
Expires = DateTime.UtcNow.AddHours(jwtConfig.Value.TokenLifetimeHours), Expires = DateTime.UtcNow.AddHours(jwtConfig.Value.TokenLifetimeHours),
Issuer = jwtConfig.Value.Issuer, Issuer = jwtConfig.Value.Issuer,
+2
View File
@@ -14,12 +14,14 @@
<Reference Include="Microsoft.AspNetCore.Http.Abstractions"> <Reference Include="Microsoft.AspNetCore.Http.Abstractions">
<HintPath>C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.8\Microsoft.AspNetCore.Http.Abstractions.dll</HintPath> <HintPath>C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.8\Microsoft.AspNetCore.Http.Abstractions.dll</HintPath>
</Reference> </Reference>
<Reference Include="Microsoft.AspNetCore.Http.Features" />
<Reference Include="Microsoft.Extensions.Options"> <Reference Include="Microsoft.Extensions.Options">
<HintPath>C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.8\Microsoft.Extensions.Options.dll</HintPath> <HintPath>C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\8.0.8\Microsoft.Extensions.Options.dll</HintPath>
</Reference> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.1.2" /> <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="7.1.2" />
</ItemGroup> </ItemGroup>
+10 -5
View File
@@ -1,7 +1,7 @@
using Azaion.Common; using Azaion.Common;
using Azaion.Common.Configs; using Azaion.Common.Configs;
using Azaion.Common.Entities; using Azaion.Common.Entities;
using Azaion.Common.Requests; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
namespace Azaion.Services; namespace Azaion.Services;
@@ -9,7 +9,7 @@ namespace Azaion.Services;
public interface IResourcesService public interface IResourcesService
{ {
Task GetEncryptedResource(ResourceEnum resource, string key, Stream outputStream, CancellationToken cancellationToken = default); Task GetEncryptedResource(ResourceEnum resource, string key, Stream outputStream, CancellationToken cancellationToken = default);
Task SaveResource(UploadResourceRequest request, CancellationToken cancellationToken = default); Task SaveResource(ResourceEnum resourceEnum, IFormFile data, CancellationToken cancellationToken = default);
} }
public class ResourcesService(IOptions<ResourcesConfig> resourcesConfig) : IResourcesService public class ResourcesService(IOptions<ResourcesConfig> resourcesConfig) : IResourcesService
@@ -20,10 +20,15 @@ public class ResourcesService(IOptions<ResourcesConfig> resourcesConfig) : IReso
await fileStream.EncryptTo(outputStream, key, cancellationToken); await fileStream.EncryptTo(outputStream, key, cancellationToken);
} }
public async Task SaveResource(UploadResourceRequest request, CancellationToken cancellationToken = default) public async Task SaveResource(ResourceEnum resourceEnum, IFormFile data, CancellationToken cancellationToken = default)
{ {
await using var fileStream = new FileStream(GetResourcePath(request.ResourceEnum), FileMode.OpenOrCreate, FileAccess.ReadWrite); if (data == null)
await request.Data.CopyToAsync(fileStream, cancellationToken); throw new BusinessException(ExceptionEnum.NoFile, "No file provided!");
if (!Directory.Exists(resourcesConfig.Value.ResourcesFolder))
Directory.CreateDirectory(resourcesConfig.Value.ResourcesFolder);
await using var fileStream = new FileStream(GetResourcePath(resourceEnum), FileMode.OpenOrCreate, FileAccess.ReadWrite);
await data.CopyToAsync(fileStream, cancellationToken);
} }
private string GetResourcePath(ResourceEnum resourceEnum) private string GetResourcePath(ResourceEnum resourceEnum)
+5 -4
View File
@@ -9,7 +9,7 @@ namespace Azaion.Services;
public interface IUserService public interface IUserService
{ {
Task RegisterUser(RegisterUserRequest request, CancellationToken cancellationToken = default); Task RegisterUser(RegisterUserRequest request, CancellationToken cancellationToken = default);
Task<User> ValidateUser(string username, string password, string? hardwareId = null, CancellationToken cancellationToken = default); Task<User> ValidateUser(LoginRequest request, string? hardwareId = null, CancellationToken cancellationToken = default);
Task UpdateHardwareId(string username, string hardwareId, CancellationToken cancellationToken = default); Task UpdateHardwareId(string username, string hardwareId, CancellationToken cancellationToken = default);
} }
@@ -25,6 +25,7 @@ public class UserService(IDbFactory dbFactory) : IUserService
await db.InsertAsync(new User await db.InsertAsync(new User
{ {
Id = Guid.NewGuid(),
Email = request.Email, Email = request.Email,
PasswordHash = request.Password.ToHash(), PasswordHash = request.Password.ToHash(),
Role = request.Role Role = request.Role
@@ -32,14 +33,14 @@ public class UserService(IDbFactory dbFactory) : IUserService
}); });
} }
public async Task<User> ValidateUser(string username, string password, string? hardwareId = null, CancellationToken cancellationToken = default) => public async Task<User> ValidateUser(LoginRequest request, string? hardwareId = null, CancellationToken cancellationToken = default) =>
await dbFactory.Run(async db => await dbFactory.Run(async db =>
{ {
var user = await db.Users.FirstOrDefaultAsync(x => x.Email == username, token: cancellationToken); var user = await db.Users.FirstOrDefaultAsync(x => x.Email == request.Email, token: cancellationToken);
if (user == null) if (user == null)
throw new BusinessException(ExceptionEnum.NoUserFound, "No user found"); throw new BusinessException(ExceptionEnum.NoUserFound, "No user found");
if (password.ToHash() != user.PasswordHash) if (request.Password.ToHash() != user.PasswordHash)
throw new BusinessException(ExceptionEnum.PasswordIncorrect, "Passwords do not match"); throw new BusinessException(ExceptionEnum.PasswordIncorrect, "Passwords do not match");
if (user.Role == RoleEnum.ApiAdmin) if (user.Role == RoleEnum.ApiAdmin)
+13
View File
@@ -0,0 +1,13 @@
-- users table
drop table users;
create table users
(
id uuid primary key,
email varchar(160) not null,
password_hash varchar(255) not null,
hardware_id varchar(120) null,
role varchar(20) not null
);
grant select, insert, update, delete on public.users to azaion_admin;
grant select on table public.users to azaion_reader;
-29
View File
@@ -1,29 +0,0 @@
create database azaion;
-- make sure you connect to azaion db
--superadmin user
create role azaion_superadmin with login password 'superadmin_pass';
grant all privileges on all tables in schema public to azaion_superadmin;
--writer user
create role azaion_admin with login password 'admin_pass';
grant connect on database azaion to azaion_admin;
grant usage on schema public to azaion_admin;
--readonly user
create role azaion_reader with login password 'reader_pass';
grant connect on database azaion to azaion_reader;
grant usage on schema public to azaion_reader;
-- users table
create table users
(
id uuid primary key,
email varchar(160) not null,
password_hash varchar(255) not null,
hardware_id varchar(120) not null,
role varchar(20) not null
);
grant select, insert, update, delete on public.users to azaion_admin;
grant select on table public.users to azaion_reader;