using System.Text; using Azaion.Common; using Azaion.Common.Configs; using Azaion.Common.Database; using Azaion.Common.Entities; using Azaion.Common.Requests; using Azaion.Services; using FluentValidation; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; using Serilog; Log.Logger = new LoggerConfiguration() .Enrich.FromLogContext() .MinimumLevel.Information() .WriteTo.Console() .WriteTo.File( path: "logs/log.txt", rollingInterval: RollingInterval.Day) .CreateLogger(); 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(); if (jwtConfig == null || string.IsNullOrEmpty(jwtConfig.Secret)) throw new Exception("Missing configuration section: JwtConfig"); var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtConfig.Secret)); builder.Services.AddSerilog(); builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(o => { o.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtConfig.Issuer, ValidAudience = jwtConfig.Audience, IssuerSigningKey = signingKey }; }); #region Policies var apiAdminPolicy = new AuthorizationPolicyBuilder() .RequireRole(RoleEnum.ApiAdmin.ToString()).Build(); var apiUploaderPolicy = new AuthorizationPolicyBuilder() .RequireRole(RoleEnum.ResourceUploader.ToString(), RoleEnum.ApiAdmin.ToString()).Build(); builder.Services.AddAuthorization(o => { o.AddPolicy(nameof(apiAdminPolicy), apiAdminPolicy); o.AddPolicy(nameof(apiUploaderPolicy), apiUploaderPolicy); }); #endregion Policies builder.Services.AddHttpContextAccessor(); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo {Title = "Azaion.API", Version = "v1"}); c.CustomSchemaIds(type => type.ToString()); var jwtSecurityScheme = new OpenApiSecurityScheme { Scheme = "bearer", BearerFormat = "JWT", Name = "JWT Authentication", In = ParameterLocation.Header, Type = SecuritySchemeType.Http, Description = "Put **_ONLY_** your JWT Bearer token on textbox below!", Reference = new OpenApiReference { Id = JwtBearerDefaults.AuthenticationScheme, Type = ReferenceType.SecurityScheme } }; c.AddSecurityDefinition(jwtSecurityScheme.Reference.Id, jwtSecurityScheme); c.AddSecurityRequirement(new OpenApiSecurityRequirement { { jwtSecurityScheme, Array.Empty() } }); }); builder.Services.Configure(builder.Configuration.GetSection(nameof(ResourcesConfig))); builder.Services.Configure(builder.Configuration.GetSection(nameof(JwtConfig))); builder.Services.Configure(builder.Configuration.GetSection(nameof(ConnectionStrings))); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddValidatorsFromAssemblyContaining(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseAuthentication(); app.UseAuthorization(); app.MapPost("/login", async (LoginRequest request, IUserService userService, IAuthService authService, CancellationToken cancellationToken) => { var user = await userService.ValidateUser(request, cancellationToken: cancellationToken); return Results.Ok(new { Token = authService.CreateToken(user)}); }) .WithOpenApi(op => new(op){ Summary = "Login"});; app.MapPost("/users", async (RegisterUserRequest registerUserRequest, IUserService userService, CancellationToken cancellationToken) => await userService.RegisterUser(registerUserRequest, cancellationToken)) .RequireAuthorization(apiAdminPolicy) .WithOpenApi(op => new(op){ Summary = "Creates a new user"}); app.MapGet("/users", async (string? searchEmail, RoleEnum? searchRole, IUserService userService, CancellationToken cancellationToken) => await userService.GetUsers(searchEmail, searchRole, cancellationToken)) .RequireAuthorization(apiAdminPolicy) .WithOpenApi(op => new(op){ Summary = "List users by criteria"}); app.MapPost("/resources", async (IFormFile data, IResourcesService resourceService, CancellationToken cancellationToken) => await resourceService.SaveResource(data, cancellationToken)) .Accepts("multipart/form-data") .RequireAuthorization(apiUploaderPolicy) .DisableAntiforgery(); app.MapPost("/resources/get", //Need to have POST method for secure password async ([FromBody]GetResourceRequest request, IAuthService authService, IUserService userService, IResourcesService resourcesService, CancellationToken cancellationToken) => { var user = authService.CurrentUser; if (user == null) throw new UnauthorizedAccessException(); await userService.CheckHardware(user, request); var key = Security.MakeEncryptionKey(user.Email, request.Password, request.Hardware.Hash); var stream = await resourcesService.GetEncryptedResource(request.FileName, key, cancellationToken); return Results.File(stream, "application/octet-stream", request.FileName); }).RequireAuthorization() .WithOpenApi(op => new OpenApiOperation(op){ Summary = "Gets encrypted by users Password and HardwareHash resources. POST method for secure password"}); app.MapPut("/resources/reset-hardware", async (string email, IUserService userService, CancellationToken cancellationToken) => await userService.UpdateHardware(email, new HardwareInfo(), cancellationToken)) .WithOpenApi(op => new OpenApiOperation(op){ Summary = "Resets hardware id in case of hardware change"}); app.Run();