diff --git a/src/Controllers/AssetsController.cs b/src/Controllers/AssetsController.cs index adb7031..1b803e5 100644 --- a/src/Controllers/AssetsController.cs +++ b/src/Controllers/AssetsController.cs @@ -28,6 +28,10 @@ public class AssetsController : Controller { var assetList = await _ldap.ListDeviceAsync(Cn); result = new AssetsGetResponseModel(successful: true, assetModel: assetList); + if (result.AssetsModel is not null) + { + result.AssetsModel.Owner = result.AssetsModel?.Owner?.Replace("uid=", ""); + } } catch (Exception e) { @@ -45,6 +49,11 @@ public class AssetsController : Controller { var assetList = await _ldap.ListDeviceAsync(); result = new AssetsGetAllResponseModel(successful: true, assetsModel: assetList); + result.AssetsModel = result.AssetsModel?.Select(asset => + { + asset.Owner = asset.Owner?.Replace("uid=", ""); + return asset; + }); } catch (Exception e) { @@ -71,13 +80,10 @@ public class AssetsController : Controller { LdapAttributeSet attributeSet = [ - new LdapAttribute("objectClass", new[] {"top", "device", "extensibleObject"}), + new LdapAttribute("objectClass", ["top", "device", "extensibleObject"]), ]; - if (assetModel.Cn != null) - { - attributeSet.Add(new LdapAttribute("cn", assetModel.Cn)); - } + attributeSet.Add(new LdapAttribute("cn", await _ldap.GetAssetCounterAndIncrementAsync())); if (assetModel.SerialNumber != null) { attributeSet.Add(new LdapAttribute("serialNumber", assetModel.SerialNumber)); @@ -168,7 +174,7 @@ public class AssetsController : Controller } if (requestModel.Owner is not null) { - await _ldap.UpdateAsset(cn, "owner", requestModel.Owner); + await _ldap.UpdateAsset(cn, "owner", $"uid={requestModel.Owner}"); } if (requestModel.SerialNumber is not null) { diff --git a/src/Models/AssetsCreateRequestModel.cs b/src/Models/AssetsCreateRequestModel.cs deleted file mode 100644 index fff5224..0000000 --- a/src/Models/AssetsCreateRequestModel.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Berufsschule_HAM.Models; - -public class AssetsCreateRequestModel -{ - public required string Cn { get; set; } - public AssetDescription? Description { get; set; } = null; - public string? Location { get; set; } = null; - public string? Name { get; set; } = null; - public string? Owner { get; set; } = null; - public string? SerialNumber { get; set; } = null; -} \ No newline at end of file diff --git a/src/Models/AssetsModel.cs b/src/Models/AssetsModel.cs index 80bf3cd..6d93c40 100644 --- a/src/Models/AssetsModel.cs +++ b/src/Models/AssetsModel.cs @@ -70,4 +70,9 @@ public class AssetsTableViewModel public required string AssetCn { get; set; } public string? AssetName { get; set; } public string? LocationName { get; set; } +} + +public class AssetsMetadataModel +{ + public int CnCounter { get; set; } } \ No newline at end of file diff --git a/src/Models/AssetsRequestModels.cs b/src/Models/AssetsRequestModels.cs index 6c63102..abd1560 100644 --- a/src/Models/AssetsRequestModels.cs +++ b/src/Models/AssetsRequestModels.cs @@ -1,15 +1,31 @@ +using System.Text.Json.Serialization; using Microsoft.AspNetCore.Mvc; namespace Berufsschule_HAM.Models; -public class AssetsModifyRequestModel +public class AssetsCreateRequestModel { - public required string Cn { get; set; } - public string? NewCn { get; set; } = null; - [FromBody] public AssetDescription? Description { get; set; } = null; public string? Location { get; set; } = null; public string? Name { get; set; } = null; public string? Owner { get; set; } = null; public string? SerialNumber { get; set; } = null; +} + +public class AssetsModifyRequestModel +{ + [JsonPropertyName("Cn")] + public required string Cn { get; set; } + [JsonPropertyName("NewCn")] + public string? NewCn { get; set; } = null; + [JsonPropertyName("Description")] + public AssetDescription? Description { get; set; } = null; + [JsonPropertyName("Location")] + public string? Location { get; set; } = null; + [JsonPropertyName("Name")] + public string? Name { get; set; } = null; + [JsonPropertyName("Owner")] + public string? Owner { get; set; } = null; + [JsonPropertyName("SerialNumber")] + public string? SerialNumber { get; set; } = null; } \ No newline at end of file diff --git a/src/Program.cs b/src/Program.cs index 9c35a95..7d4a7dd 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -32,7 +32,7 @@ builder.Services.AddElmah(Options => }); builder.Services.AddSingleton(); -builder.Services.AddSingleton(); +builder.Services.AddHostedService(); builder.Services.AddHealthChecks() .AddCheck("DatabaseHealthCheck"); @@ -123,8 +123,5 @@ app.MapControllerRoute( app.MapHealthChecks("/healthz") .RequireAuthorization(); -// Run migrations -using var scope = app.Services.CreateScope(); -var migrationService = scope.ServiceProvider.GetRequiredService(); app.Run(); diff --git a/src/Services/LdapService.cs b/src/Services/LdapService.cs index 2ff006d..28b1311 100644 --- a/src/Services/LdapService.cs +++ b/src/Services/LdapService.cs @@ -118,6 +118,51 @@ public partial class LdapService : IDisposable return true; } + public async Task GetAssetCounterAndIncrementAsync() + { + AssetsMetadataModel assetModel = await GetAssetCounterAsync(); + string returnValue = assetModel.CnCounter.ToString(); + assetModel.CnCounter++; + await UpdateAssetCounterAsync(assetModel); + return returnValue; + } + + public async Task GetAssetCounterAsync() + { + Dictionary entry = (await ListObjectBy(_opts.BaseDn, _opts.AssetsOu, ["description"])).First(); + try + { + string description = entry["description"]; + return JsonSerializer.Deserialize(description) ?? throw new Exception($"Unable to deserialize description: {description}"); + } + catch (Exception ex) + { + _logger.LogError(ex, "Unable to retrieve asset counter due to exception: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]); + throw; + } + } + + public async Task UpdateAssetCounterAsync(AssetsMetadataModel assetMetadataModel) + { + await ConnectAndBind(); + try + { + string dn = AssetsBaseDn; //PrependRDN($"", MigrationsBaseDn); + string targetText = JsonSerializer.Serialize(assetMetadataModel); + _logger.LogInformation("Setting the LDAP asset counter to {targetText} for {dn}", [targetText, dn]); + var modification = new LdapModification( + LdapModification.Replace, + new LdapAttribute("description", targetText) + ); + await _conn.ModifyAsync(dn, modification); + } + catch (Exception) + { + return false; + } + return true; + } + public async Task> ListUsersAsync(string[] attributes) { List returnValue = []; @@ -396,6 +441,11 @@ public async Task CreateAsset(LdapAttributeSet attributeSet) await _conn.AddAsync(ldapEntry); } + public async Task ModifyAsync(string dn, LdapModification ldapModification) + { + await _conn.ModifyAsync(dn, ldapModification); + } + public void Dispose() { if (_conn != null && _conn.Connected) diff --git a/src/Services/MigrationService.cs b/src/Services/MigrationService.cs index b5717cd..8136c38 100644 --- a/src/Services/MigrationService.cs +++ b/src/Services/MigrationService.cs @@ -2,9 +2,10 @@ using Berufsschule_HAM.Models; using Microsoft.Extensions.Options; using Novell.Directory.Ldap; using System.Reflection; +using System.Text.Json; namespace Berufsschule_HAM.Services; -public class MigrationService +public class MigrationService : IHostedService { private readonly LdapService _ldapService; private readonly ILogger _logger; @@ -15,8 +16,15 @@ public class MigrationService _ldapService = ldapService; _logger = logger; _ldapConfig = ldapConfig.Value; - MigrateAsync().ConfigureAwait(false); } + + public async Task StartAsync(CancellationToken cancellationToken) + { + await MigrateAsync(); + } + + public async Task StopAsync(CancellationToken cancellationToken) { } + public async Task MigrateAsync() { MigrationModel migrationModel = await _ldapService.GetMigrationVersionAsync(); @@ -37,18 +45,13 @@ public class MigrationService { _logger.LogInformation("Migrating LDAP database from version {version} to {methodVersion}", [version, methodVersion + 1]); if (method is null) { continue; } -#pragma warning disable CS8605 // Unboxing a possibly null value. - version = (int)method.Invoke(null, [_ldapService]); -#pragma warning restore CS8605 // Unboxing a possibly null value. + Task? invocation = (Task?)method.Invoke(null, [_ldapService]) ?? throw new Exception("Invocation is null"); + version = await invocation; + migrationModel.Version = version; + await _ldapService.UpdateMigrationVersionAsync(migrationModel); } } - if (version != migrationModel.Version) - { - migrationModel.Version = version; - await _ldapService.UpdateMigrationVersionAsync(migrationModel); - } - return migrationModel; } @@ -92,6 +95,15 @@ public class MigrationService await TryCreateObjectIgnoreAlreadyExists(ldapService, ldapService.MigrationsBaseDn, migrationsAttributes); return 1; } + public static async Task UpdateFrom1Async(LdapService ldapService) + { + // Add the description attribute to ou=assets + AssetsMetadataModel assetsMetadataModel = new() { CnCounter = 1 }; + LdapAttribute ldapAttribute = new("description", JsonSerializer.Serialize(assetsMetadataModel)); + LdapModification ldapModification = new(LdapModification.Add, ldapAttribute); + await ldapService.ModifyAsync(ldapService.AssetsBaseDn, ldapModification); + return 2; + } private static async Task TryCreateObjectIgnoreAlreadyExists(LdapService ldapService, string dn, LdapAttributeSet ldapAttributes) { diff --git a/src/Views/Home/Assets.cshtml b/src/Views/Home/Assets.cshtml index 8dfc3ca..debcf0e 100644 --- a/src/Views/Home/Assets.cshtml +++ b/src/Views/Home/Assets.cshtml @@ -182,10 +182,6 @@