diff --git a/dependencies.props b/dependencies.props index e67b266..11b84d3 100644 --- a/dependencies.props +++ b/dependencies.props @@ -8,6 +8,7 @@ 5.0.0 1.0.2 1.0.7 + 1.1.3 10.1.1 3.3.0 5.0.7 diff --git a/src/integration/ProxmoxConnector.Integration.Abstractions/IPveConnector.cs b/src/integration/ProxmoxConnector.Integration.Abstractions/IPveConnector.cs index 0f11bff..ec2f48f 100644 --- a/src/integration/ProxmoxConnector.Integration.Abstractions/IPveConnector.cs +++ b/src/integration/ProxmoxConnector.Integration.Abstractions/IPveConnector.cs @@ -1,6 +1,10 @@ -namespace ProxmoxConnector.Integration.Abstractions +using System.Threading.Tasks; + +namespace ProxmoxConnector.Integration.Abstractions { public interface IPveConnector { + Task TestWithLogin(); + Task TestWithToken(); } } diff --git a/src/integration/ProxmoxConnector.Integration.Corsinvest/Services/PveConnector.cs b/src/integration/ProxmoxConnector.Integration.Corsinvest/Services/PveConnector.cs index 740b2ab..5672ce5 100644 --- a/src/integration/ProxmoxConnector.Integration.Corsinvest/Services/PveConnector.cs +++ b/src/integration/ProxmoxConnector.Integration.Corsinvest/Services/PveConnector.cs @@ -8,10 +8,10 @@ namespace ProxmoxConnector.Integration.Corsinvest.Services { internal class PveConnector : IPveConnector { - public async Task Test() + public async Task TestWithLogin() { var client = new PveClient("***REMOVED***"); ***REMOVED*** - if (await client.Login("root", "password")) + if (await client.Login(***REMOVED***)) { var vm = client.Nodes["pve1"].Qemu[100]; @@ -40,7 +40,10 @@ namespace ProxmoxConnector.Integration.Corsinvest.Services var dataImg = (await client.Nodes["pve1"].Rrd.Rrd("cpu", "day")).Response; Console.WriteLine(""); } + } + public async Task TestWithToken() + { //using Api Token var client2 = new PveClient("10.92.100.33"); client2.ApiToken = "root@pam!qqqqqq=***REMOVED***"; diff --git a/src/server/ProxmoxConnector.Server.Application/Class1.cs b/src/server/ProxmoxConnector.Server.Application/Class1.cs deleted file mode 100644 index e066fea..0000000 --- a/src/server/ProxmoxConnector.Server.Application/Class1.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace ProxmoxConnector.Server.Application -{ - public class Class1 - { - } -} diff --git a/src/server/ProxmoxConnector.Server.Application/DependencyInjectionExtensions.cs b/src/server/ProxmoxConnector.Server.Application/DependencyInjectionExtensions.cs new file mode 100644 index 0000000..dcc5a9b --- /dev/null +++ b/src/server/ProxmoxConnector.Server.Application/DependencyInjectionExtensions.cs @@ -0,0 +1,14 @@ +using Microsoft.Extensions.DependencyInjection; +using ProxmoxConnector.Integration.Corsinvest; +using System; + +namespace ProxmoxConnector.Server.Application +{ + public static class DependencyInjectionExtensions + { + public static void AddApplicationServices(this IServiceCollection services) + { + services.AddCorsinvestPveConnector(); + } + } +} diff --git a/src/server/ProxmoxConnector.Server.Application/Mappings/MappingProfile.cs b/src/server/ProxmoxConnector.Server.Application/Mappings/MappingProfile.cs new file mode 100644 index 0000000..1ee4bc1 --- /dev/null +++ b/src/server/ProxmoxConnector.Server.Application/Mappings/MappingProfile.cs @@ -0,0 +1,12 @@ +using AutoMapper; + +namespace ProxmoxConnector.Server.Application.Mappings +{ + public class MappingProfile : Profile + { + public MappingProfile() + { + //CreateMap(); + } + } +} diff --git a/src/server/ProxmoxConnector.Server.Application/ProxmoxConnector.Server.Application.csproj b/src/server/ProxmoxConnector.Server.Application/ProxmoxConnector.Server.Application.csproj index 0825fe9..ce8567b 100644 --- a/src/server/ProxmoxConnector.Server.Application/ProxmoxConnector.Server.Application.csproj +++ b/src/server/ProxmoxConnector.Server.Application/ProxmoxConnector.Server.Application.csproj @@ -12,5 +12,10 @@ + + + + + diff --git a/src/server/ProxmoxConnector.Server.Application/Services/Environment/EnvironmentConfigurator.cs b/src/server/ProxmoxConnector.Server.Application/Services/Environment/EnvironmentConfigurator.cs new file mode 100644 index 0000000..16821fa --- /dev/null +++ b/src/server/ProxmoxConnector.Server.Application/Services/Environment/EnvironmentConfigurator.cs @@ -0,0 +1,40 @@ +using Microsoft.Extensions.Configuration; +using System; +using System.Data.Common; +using System.IO; + +namespace ProxmoxConnector.Server.Application.Services.Environment +{ + public class EnvironmentConfigurator + { + private readonly string _workspace; + private readonly string _databaseConnection; + + public EnvironmentConfigurator(IConfigurationRoot configuration) + { + _workspace = configuration.GetValue("Workspace"); + _databaseConnection = configuration.GetConnectionString("DatabaseConnection"); + } + + public void Configure() + { + if (string.IsNullOrEmpty(_workspace)) + throw new Exception($"Workspace path is empty! Check 'Workspace' parameter."); + + if (!Directory.Exists(_workspace)) + Directory.CreateDirectory(_workspace); + } + + public (string databasePath, string workspace) GetStorageTools() + { + var builder = new DbConnectionStringBuilder + { + ConnectionString = _databaseConnection + }; + + var dataSource = builder["Data Source"].ToString(); + var databasePath = dataSource.Replace("{Workspace}", _workspace); + return (databasePath, _workspace); + } + } +} diff --git a/src/server/ProxmoxConnector.Server.Domain/Abstractions/IUserService.cs b/src/server/ProxmoxConnector.Server.Domain/Abstractions/IUserService.cs new file mode 100644 index 0000000..10c6996 --- /dev/null +++ b/src/server/ProxmoxConnector.Server.Domain/Abstractions/IUserService.cs @@ -0,0 +1,10 @@ +namespace ProxmoxConnector.Server.Domain.Abstractions +{ + public interface IUserService + { + bool UserIsLoggedIn { get; } + string GetUserId(); + string GetUserName(); + bool UserIsGuest(); + } +} diff --git a/src/server/ProxmoxConnector.Server/Controllers/HealthController.cs b/src/server/ProxmoxConnector.Server/Controllers/HealthController.cs new file mode 100644 index 0000000..50d03b5 --- /dev/null +++ b/src/server/ProxmoxConnector.Server/Controllers/HealthController.cs @@ -0,0 +1,28 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; + +namespace ProxmoxConnector.Server.Controllers +{ + [Authorize] + [ApiController] + [Route("health")] + public class HealthController : ControllerBase + { + private readonly ILogger _logger; + + public HealthController(ILogger logger) + { + _logger = logger; + } + + [AllowAnonymous] + [HttpGet("ping")] + public IActionResult Ping() + { + _logger.LogInformation("Ping"); + return Ok($"System datetime: {DateTime.Now}"); + } + } +} diff --git a/src/server/ProxmoxConnector.Server/Controllers/WeatherForecastController.cs b/src/server/ProxmoxConnector.Server/Controllers/WeatherForecastController.cs deleted file mode 100644 index 26f1eb5..0000000 --- a/src/server/ProxmoxConnector.Server/Controllers/WeatherForecastController.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace ProxmoxConnector.Server.Controllers -{ - [ApiController] - [Route("[controller]")] - public class WeatherForecastController : ControllerBase - { - private static readonly string[] Summaries = new[] - { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" - }; - - private readonly ILogger _logger; - - public WeatherForecastController(ILogger logger) - { - _logger = logger; - } - - [HttpGet] - public IEnumerable Get() - { - var rng = new Random(); - return Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = DateTime.Now.AddDays(index), - TemperatureC = rng.Next(-20, 55), - Summary = Summaries[rng.Next(Summaries.Length)] - }) - .ToArray(); - } - } -} diff --git a/src/server/ProxmoxConnector.Server/Program.cs b/src/server/ProxmoxConnector.Server/Program.cs index d71492b..7ffa91d 100644 --- a/src/server/ProxmoxConnector.Server/Program.cs +++ b/src/server/ProxmoxConnector.Server/Program.cs @@ -1,11 +1,14 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; +using ProxmoxConnector.Server.Application.Services.Environment; +using Serilog; +using Serilog.Core; +using Serilog.Events; using System; -using System.Collections.Generic; +using System.Diagnostics; +using System.IO; using System.Linq; -using System.Threading.Tasks; namespace ProxmoxConnector.Server { @@ -13,14 +16,69 @@ namespace ProxmoxConnector.Server { public static void Main(string[] args) { - CreateHostBuilder(args).Build().Run(); + var isContainer = args.Contains("--docker"); + if (!isContainer) + { + var pathToExe = Process.GetCurrentProcess().MainModule.FileName; + var pathToContentRoot = Path.GetDirectoryName(pathToExe); + Directory.SetCurrentDirectory(pathToContentRoot); + } + + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true, reloadOnChange: true) + .AddEnvironmentVariables() + .Build(); + + var configurator = new EnvironmentConfigurator(configuration); + configurator.Configure(); + var (databasePath, workspace) = configurator.GetStorageTools(); + + var loggingLevelParam = configuration.GetValue("Logging:LogLevel:Default"); + if (!Enum.TryParse(loggingLevelParam, out LogEventLevel loggingLevel)) + throw new Exception($"Unknown logging level '{loggingLevelParam}'."); + + var loggingLevelSwitch = new LoggingLevelSwitch(loggingLevel); + + Log.Logger = new LoggerConfiguration() + .MinimumLevel.ControlledBy(loggingLevelSwitch) + .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.SQLite(databasePath, "__Logs", levelSwitch: loggingLevelSwitch) + .CreateLogger(); + + try + { + var urls = configuration.GetValue("urls"); + Log.Information("Starting Proxmox connector..."); + Log.Information($"API listening on {urls}"); + Log.Information($"Workspace configured at '{workspace}' path"); + Console.WriteLine("Application started. Press Ctrl+C to shut down."); + CreateHostBuilder(args, configuration).Build().Run(); + } + catch (Exception ex) + { + Log.Fatal(ex, "CDN Server host terminated unexpectedly"); + } + finally + { + Log.CloseAndFlush(); + } } - public static IHostBuilder CreateHostBuilder(string[] args) => - Host.CreateDefaultBuilder(args) + public static IHostBuilder CreateHostBuilder(string[] args, IConfiguration configuration) + { + var builder = Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { - webBuilder.UseStartup(); + webBuilder.UseStartup() + .UseConfiguration(configuration) + .UseSerilog(); }); + + return builder; + } } } diff --git a/src/server/ProxmoxConnector.Server/Properties/launchSettings.json b/src/server/ProxmoxConnector.Server/Properties/launchSettings.json index 2536590..99303cb 100644 --- a/src/server/ProxmoxConnector.Server/Properties/launchSettings.json +++ b/src/server/ProxmoxConnector.Server/Properties/launchSettings.json @@ -1,22 +1,6 @@ { "$schema": "http://json.schemastore.org/launchsettings.json", - "iisSettings": { - "windowsAuthentication": false, - "anonymousAuthentication": true, - "iisExpress": { - "applicationUrl": "http://localhost:65222", - "sslPort": 0 - } - }, "profiles": { - "IIS Express": { - "commandName": "IISExpress", - "launchBrowser": true, - "launchUrl": "swagger", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - }, "ProxmoxConnector.Server": { "commandName": "Project", "dotnetRunMessages": "true", diff --git a/src/server/ProxmoxConnector.Server/ProxmoxConnector.Server.csproj b/src/server/ProxmoxConnector.Server/ProxmoxConnector.Server.csproj index d870710..cd94eb2 100644 --- a/src/server/ProxmoxConnector.Server/ProxmoxConnector.Server.csproj +++ b/src/server/ProxmoxConnector.Server/ProxmoxConnector.Server.csproj @@ -1,4 +1,4 @@ - + net5.0 @@ -8,9 +8,15 @@ + + + + + + diff --git a/src/server/ProxmoxConnector.Server/Services/UserService.cs b/src/server/ProxmoxConnector.Server/Services/UserService.cs new file mode 100644 index 0000000..c9644e8 --- /dev/null +++ b/src/server/ProxmoxConnector.Server/Services/UserService.cs @@ -0,0 +1,46 @@ +using Microsoft.AspNetCore.Http; +using ProxmoxConnector.Server.Domain.Abstractions; +using System; +using System.Linq; +using System.Security.Claims; + +namespace ProxmoxConnector.Server.Services +{ + public class UserService : IUserService + { + private readonly IHttpContextAccessor _httpAccessor; + + public UserService(IHttpContextAccessor httpAccessor) + { + _httpAccessor = httpAccessor; + } + + public bool UserIsLoggedIn => _httpAccessor.HttpContext.User != null; + + public string GetUserId() + { + var userId = _httpAccessor.HttpContext.User?.Claims.FirstOrDefault(z => z.Type == ClaimTypes.NameIdentifier)?.Value; + + if (string.IsNullOrEmpty(userId)) + throw new Exception("User id could not be retrieved from claims."); + + return userId; + } + + public string GetUserName() + { + var userName = _httpAccessor.HttpContext.User?.Claims.FirstOrDefault(z => z.Type == ClaimTypes.Name)?.Value; + + if (string.IsNullOrEmpty(userName)) + throw new Exception("User name could not be retrieved from claims."); + + return userName; + } + + public bool UserIsGuest() + { + var userIsGuest = _httpAccessor.HttpContext.User?.Claims.FirstOrDefault(z => z.Type == NDB.Security.Authentication.Identity.Constants.ClaimTypes.IsGuestUser)?.Value; + return !string.IsNullOrEmpty(userIsGuest) && bool.TrueString == userIsGuest; + } + } +} diff --git a/src/server/ProxmoxConnector.Server/Startup.cs b/src/server/ProxmoxConnector.Server/Startup.cs index 6f41cb5..40297b5 100644 --- a/src/server/ProxmoxConnector.Server/Startup.cs +++ b/src/server/ProxmoxConnector.Server/Startup.cs @@ -6,6 +6,14 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.OpenApi.Models; +using NDB.Extensions.Swagger; +using NDB.Extensions.Swagger.Constants; +using NDB.Infrastructure.DatabaseMigration; +using NDB.Security.Authentication.Identity; +using Newtonsoft.Json; +using ProxmoxConnector.Server.Application; +using ProxmoxConnector.Server.Domain.Abstractions; +using ProxmoxConnector.Server.Services; using System; using System.Collections.Generic; using System.Linq; @@ -15,42 +23,51 @@ namespace ProxmoxConnector.Server { public class Startup { + private readonly IConfiguration _configuration; + public Startup(IConfiguration configuration) { - Configuration = configuration; + _configuration = configuration; } - public IConfiguration Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.AddControllers().AddNewtonsoftJson(o => o.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc); - services.AddControllers(); - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "ProxmoxConnector.Server", Version = "v1" }); - }); + // Add basic authentication + services.AddIdentityAuthentication(_configuration.GetSection("IdentityServer")["BaseAddress"]); + + services.AddHttpContextAccessor(); + services.AddScoped(); + services.AddSwagger("ProxmoxConnector.Server", AuthorizationType.InhouseIdentity); + + services.AddAutoMapper(typeof(Application.Mappings.MappingProfile).Assembly); + + var workspace = _configuration.GetValue("Workspace"); + services.AddMigration(workspace: workspace); + + //services.AddDataAccess(); + services.AddApplicationServices(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "ProxmoxConnector.Server v1")); - } + app.UseCors(x => x.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader()); + app.UseExceptionHandler("/error"); + + app.ConfigureSwagger("CDN.Server v1"); app.UseRouting(); - + app.UseAuthentication(); app.UseAuthorization(); - app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); + + app.UseMigration(); } } } diff --git a/src/server/ProxmoxConnector.Server/WeatherForecast.cs b/src/server/ProxmoxConnector.Server/WeatherForecast.cs deleted file mode 100644 index 5e35eed..0000000 --- a/src/server/ProxmoxConnector.Server/WeatherForecast.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace ProxmoxConnector.Server -{ - public class WeatherForecast - { - public DateTime Date { get; set; } - - public int TemperatureC { get; set; } - - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - - public string Summary { get; set; } - } -} diff --git a/src/server/ProxmoxConnector.Server/appsettings.json b/src/server/ProxmoxConnector.Server/appsettings.json index d9d9a9b..cb8e87e 100644 --- a/src/server/ProxmoxConnector.Server/appsettings.json +++ b/src/server/ProxmoxConnector.Server/appsettings.json @@ -1,10 +1,19 @@ { + "urls": "http://*:7000", + "ConnectionStrings": { + "DatabaseConnection": "Data Source={Workspace}\\ProxmoxConnector.db" + }, "Logging": { "LogLevel": { - "Default": "Information", + "Default": "Debug", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, - "AllowedHosts": "*" -} + "AllowedHosts": "*", + "IdentityServer": { + //"BaseAddress": "http://localhost:5063/" + "BaseAddress": "https://toodle.ddns.net/identity-server-api/" + }, + "Workspace": "Workspace" +} \ No newline at end of file