From 4445fcd6737b0220a72f47038e54b21900086eeb Mon Sep 17 00:00:00 2001 From: Alex Bezdieniezhnykh Date: Wed, 13 Nov 2024 00:24:09 +0200 Subject: [PATCH] get resource works --- Azaion.Api/Program.cs | 24 +++++++++++++++----- Azaion.Common/Entities/ResourceEnum.cs | 1 + Azaion.Common/Requests/GetResourceRequest.cs | 13 +++++++++++ Azaion.Services/ResourcesService.cs | 9 ++++++-- Azaion.Services/UserService.cs | 6 ++--- env/01_db_permissions.sql | 16 +++++++++++++ 6 files changed, 58 insertions(+), 11 deletions(-) create mode 100644 env/01_db_permissions.sql diff --git a/Azaion.Api/Program.cs b/Azaion.Api/Program.cs index 50bc8c0..54d9cf4 100644 --- a/Azaion.Api/Program.cs +++ b/Azaion.Api/Program.cs @@ -6,6 +6,7 @@ using Azaion.Common.Entities; using Azaion.Common.Requests; using Azaion.Services; using FluentValidation; +using FluentValidation.AspNetCore; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.IdentityModel.Tokens; @@ -78,7 +79,6 @@ builder.Services.AddSingleton(); builder.Services.AddValidatorsFromAssemblyContaining(); - var app = builder.Build(); if (app.Environment.IsDevelopment()) @@ -110,20 +110,32 @@ app.MapPost("/resources", .RequireAuthorization(apiAdminPolicy) .DisableAntiforgery(); +app.MapPost("/resources/reset-hardware", + async (string email, IUserService userService, CancellationToken cancellationToken) + => await userService.UpdateHardwareId(email, null!, cancellationToken)); + + app.MapPost("/resources/get", async (GetResourceRequest request, IAuthService authService, IUserService userService, IResourcesService resourcesService, CancellationToken cancellationToken) => { var user = authService.CurrentUser; - if (user?.HardwareId != request.HardwareId) - throw new BusinessException(ExceptionEnum.HardwareIdMismatch, "Hardware mismatch! You are not authorized to access this resource from this hardware."); + if (user == null) + throw new UnauthorizedAccessException(); if (string.IsNullOrEmpty(user.HardwareId)) + { await userService.UpdateHardwareId(user.Email, request.HardwareId); + user.HardwareId = request.HardwareId; + } + + if (user.HardwareId != request.HardwareId) + throw new BusinessException(ExceptionEnum.HardwareIdMismatch, "Hardware mismatch! You are not authorized to access this resource from this hardware."); var ms = new MemoryStream(); var key = Security.MakeEncryptionKey(user.Email, request.Password); - await resourcesService.GetEncryptedResource(request.ResourceEnum, key, ms, cancellationToken); - return ms; + var filename = await resourcesService.GetEncryptedResource(request.ResourceEnum, key, ms, cancellationToken); + + return Results.File(ms, "application/octet-stream", filename); }).RequireAuthorization(); -app.Run(); \ No newline at end of file +app.Run(); diff --git a/Azaion.Common/Entities/ResourceEnum.cs b/Azaion.Common/Entities/ResourceEnum.cs index 682d4ab..59f8f14 100644 --- a/Azaion.Common/Entities/ResourceEnum.cs +++ b/Azaion.Common/Entities/ResourceEnum.cs @@ -2,6 +2,7 @@ namespace Azaion.Common.Entities; public enum ResourceEnum { + None = 0, AnnotatorDll = 10, AIModelRKNN = 20, AIModelONNX = 30, diff --git a/Azaion.Common/Requests/GetResourceRequest.cs b/Azaion.Common/Requests/GetResourceRequest.cs index 3889648..8af3440 100644 --- a/Azaion.Common/Requests/GetResourceRequest.cs +++ b/Azaion.Common/Requests/GetResourceRequest.cs @@ -1,4 +1,5 @@ using Azaion.Common.Entities; +using FluentValidation; namespace Azaion.Common.Requests; @@ -7,4 +8,16 @@ public class GetResourceRequest public string Password { get; set; } = null!; public string HardwareId { get; set; } = null!; public ResourceEnum ResourceEnum { get; set; } +} + +public class GetResourceRequestValidator : AbstractValidator +{ + public GetResourceRequestValidator() + { + RuleFor(r => r.Password) + .MinimumLength(8).WithErrorCode(ExceptionEnum.PasswordLengthIncorrect.ToString()).WithMessage("Password should be at least 8 characters."); + + RuleFor(r => r.HardwareId) + .NotEmpty().WithErrorCode(ExceptionEnum.HardwareIdMismatch.ToString()).WithMessage("Hardware Id should be not empty."); + } } \ No newline at end of file diff --git a/Azaion.Services/ResourcesService.cs b/Azaion.Services/ResourcesService.cs index 68536ff..6aef165 100644 --- a/Azaion.Services/ResourcesService.cs +++ b/Azaion.Services/ResourcesService.cs @@ -1,6 +1,8 @@ using Azaion.Common; using Azaion.Common.Configs; +using Azaion.Common.Database; using Azaion.Common.Entities; +using LinqToDB; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; @@ -8,16 +10,19 @@ namespace Azaion.Services; 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(ResourceEnum resourceEnum, IFormFile data, CancellationToken cancellationToken = default); } public class ResourcesService(IOptions resourcesConfig) : IResourcesService { - public async Task GetEncryptedResource(ResourceEnum resource, string key, Stream outputStream, CancellationToken cancellationToken = default) + public async Task GetEncryptedResource(ResourceEnum resource, string key, Stream outputStream, CancellationToken cancellationToken = default) { var fileStream = new FileStream(GetResourcePath(resource), FileMode.Open, FileAccess.Read); await fileStream.EncryptTo(outputStream, key, cancellationToken); + outputStream.Seek(0, SeekOrigin.Begin); + var name = resourcesConfig.Value.Resources.GetValueOrDefault(resource.ToString()) ?? "unknown.resource"; + return name; } public async Task SaveResource(ResourceEnum resourceEnum, IFormFile data, CancellationToken cancellationToken = default) diff --git a/Azaion.Services/UserService.cs b/Azaion.Services/UserService.cs index 07b2ff8..ee994af 100644 --- a/Azaion.Services/UserService.cs +++ b/Azaion.Services/UserService.cs @@ -10,7 +10,7 @@ public interface IUserService { Task RegisterUser(RegisterUserRequest request, CancellationToken cancellationToken = default); Task ValidateUser(LoginRequest request, string? hardwareId = null, CancellationToken cancellationToken = default); - Task UpdateHardwareId(string username, string hardwareId, CancellationToken cancellationToken = default); + Task UpdateHardwareId(string email, string hardwareId, CancellationToken cancellationToken = default); } public class UserService(IDbFactory dbFactory) : IUserService @@ -52,7 +52,7 @@ public class UserService(IDbFactory dbFactory) : IUserService return user; }); - public async Task UpdateHardwareId(string username, string hardwareId, CancellationToken cancellationToken = default) => + public async Task UpdateHardwareId(string email, string hardwareId, CancellationToken cancellationToken = default) => await dbFactory.RunAdmin(async db => - await db.Users.UpdateAsync(x => x.Email == username, u => new User { HardwareId = hardwareId}, token: cancellationToken)); + await db.Users.UpdateAsync(x => x.Email == email, u => new User { HardwareId = hardwareId}, token: cancellationToken)); } diff --git a/env/01_db_permissions.sql b/env/01_db_permissions.sql new file mode 100644 index 0000000..e9a7255 --- /dev/null +++ b/env/01_db_permissions.sql @@ -0,0 +1,16 @@ +create database azaion; +-- make sure you connect to azaion db + +--superadmin user (only for db managing) +create role azaion_superadmin with login password 'superadmin-pass'; +grant all on 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 'readonly-pass'; +grant connect on database azaion to azaion_reader; +grant usage on schema public to azaion_reader; \ No newline at end of file