diff --git a/CSLabs.Api/Jobs/ApiTokenJob.cs b/CSLabs.Api/Jobs/ApiTokenJob.cs new file mode 100644 index 0000000..dd84be3 --- /dev/null +++ b/CSLabs.Api/Jobs/ApiTokenJob.cs @@ -0,0 +1,28 @@ +using System; +using System.Threading.Tasks; +using CSLabs.Api.Models; +using CSLabs.Api.Services; +using FluentScheduler; +using Microsoft.Extensions.DependencyInjection; + +namespace CSLabs.Api.Jobs +{ + public class ApiTokenJob : AsyncJob + { + private readonly IServiceProvider _serviceProvider; + + public ApiTokenJob(IServiceProvider provider) + { + _serviceProvider = provider; + } + + protected override async Task ExecuteAsync() + { + using var scope = _serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetService(); + var service = scope.ServiceProvider.GetService(); + + await service.ManageApiToken(context); + } + } +} \ No newline at end of file diff --git a/CSLabs.Api/Jobs/JobRegistry.cs b/CSLabs.Api/Jobs/JobRegistry.cs index b686db2..f6c0575 100644 --- a/CSLabs.Api/Jobs/JobRegistry.cs +++ b/CSLabs.Api/Jobs/JobRegistry.cs @@ -11,7 +11,7 @@ public JobRegistry(IServiceProvider provider) // Schedule new jobs here Schedule(() => new ExampleJob(provider)).ToRunEvery(1).Minutes(); - + Schedule(() => new ApiTokenJob(provider)).ToRunOnceAt(DateTime.Now).AndEvery(30).Days().At(0, 0); } } } \ No newline at end of file diff --git a/CSLabs.Api/Proxmox/ProxmoxApi.cs b/CSLabs.Api/Proxmox/ProxmoxApi.cs index 1c55d89..710d655 100644 --- a/CSLabs.Api/Proxmox/ProxmoxApi.cs +++ b/CSLabs.Api/Proxmox/ProxmoxApi.cs @@ -7,6 +7,7 @@ using Corsinvest.ProxmoxVE.Api; using Corsinvest.ProxmoxVE.Api.Extension.Info; using CSLabs.Api.Models.HypervisorModels; +using CSLabs.Api.Models.UserModels; using CSLabs.Api.Proxmox.Responses; using Newtonsoft.Json; @@ -17,6 +18,7 @@ public class ProxmoxApi private PveClient client; private DateTime _loggedInAt = DateTime.MinValue; private string _password; + public HypervisorNode HypervisorNode { get;} public ProxmoxApi(HypervisorNode hypervisorNode, string password) { @@ -40,6 +42,49 @@ private async Task Login() _loggedInAt = DateTime.Now; } + public async Task ManageApiToken() + { + await LoginIfNotLoggedIn(); + var userid = $"{HypervisorNode.Hypervisor.UserName}@pam"; + if (string.IsNullOrEmpty(client.ApiToken)) + await GenerateApiToken(userid); + else + await RotateApiToken(userid); + } + + private async Task RotateApiToken(string userid) + { + await LoginIfNotLoggedIn(); + await PerformRequest(() => client.Access.Users[userid].Token["API_TOKEN"].RemoveToken()); + await GenerateApiToken(userid); + } + + private async Task GenerateApiToken(string userid) + { + await LoginIfNotLoggedIn(); + var apiTokenResponse = await GetApiToken(userid); + client.ApiToken = $"{userid}!API_TOKEN={apiTokenResponse.Value}"; + } + + private async Task GetApiToken(string userid) + { + await LoginIfNotLoggedIn(); + var expireDate = (int) DateTimeOffset.UtcNow.AddDays(30).ToUnixTimeSeconds(); + var apiKey = await PerformRequest(() => + client.Access.Users[userid].Token["API_TOKEN"].GenerateToken(expire: expireDate, privsep: true)); + + return new ApiTokenResponse() + { + FullTokenId = ((IDictionary) apiKey.Response.data)["full-tokenid"].ToString(), + Info = new TokenInfo() + { + Expire = int.Parse(apiKey.Response.data.info.expire), + PrivSep = apiKey.Response.data.info.privsep == "1" + }, + Value = apiKey.Response.data.value + }; + } + public async Task GetTicket(int vmId) { await LoginIfNotLoggedIn(); diff --git a/CSLabs.Api/Proxmox/Responses/ApiTokenResponse.cs b/CSLabs.Api/Proxmox/Responses/ApiTokenResponse.cs new file mode 100644 index 0000000..54efd8b --- /dev/null +++ b/CSLabs.Api/Proxmox/Responses/ApiTokenResponse.cs @@ -0,0 +1,16 @@ +namespace CSLabs.Api.Proxmox.Responses +{ + public class ApiTokenResponse + { + public string Value { get; set; } + public TokenInfo Info { get; set; } + public string FullTokenId { get; set; } + } + + public class TokenInfo + { + public string Comment { get; set; } + public int Expire { get; set; } + public bool PrivSep { get; set; } + } +} \ No newline at end of file diff --git a/CSLabs.Api/Services/ProxmoxApiTokenService.cs b/CSLabs.Api/Services/ProxmoxApiTokenService.cs new file mode 100644 index 0000000..d7fe579 --- /dev/null +++ b/CSLabs.Api/Services/ProxmoxApiTokenService.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using CSLabs.Api.Models; +using CSLabs.Api.Proxmox; +using Microsoft.EntityFrameworkCore; + +namespace CSLabs.Api.Services +{ + public class ProxmoxApiTokenService + { + public ProxmoxManager ProxmoxManager { get;} + + public ProxmoxApiTokenService(ProxmoxManager manager) + { + ProxmoxManager = manager; + } + + public async Task ManageApiToken(DefaultContext context) + { + var hypervisor = await context.Hypervisors.FirstOrDefaultAsync(); + var primaryHypervisorNode = await ProxmoxManager.GetPrimaryHypervisorNode(hypervisor); + var api = ProxmoxManager.GetProxmoxApi(primaryHypervisorNode); + await api.ManageApiToken(); + } + } +} \ No newline at end of file diff --git a/CSLabs.Api/Services/ServiceProvider.cs b/CSLabs.Api/Services/ServiceProvider.cs index 1934d5a..efd9b2e 100644 --- a/CSLabs.Api/Services/ServiceProvider.cs +++ b/CSLabs.Api/Services/ServiceProvider.cs @@ -32,6 +32,7 @@ public static void ProvideAppServices(this IServiceCollection services) services.AddScoped(); services.ProvideProxmoxApi(); services.AddScoped(); + services.AddScoped(); services.AddTransient(); services.AddTransient(); services.AddSingleton();