From b262a73d9ddf9af373f0d7e1d98f2185859bc602 Mon Sep 17 00:00:00 2001 From: LD-Reborn Date: Fri, 3 Oct 2025 23:07:55 +0200 Subject: [PATCH] Added Assets update CRUD element --- src/Controllers/AssetsController.cs | 66 +++++++++++++++++++++-- src/Exceptions/ConfigurationExceptions.cs | 4 +- src/Models/Asset.cs | 10 ---- src/Models/AssetsModel.cs | 46 ++++++++++++++++ src/Models/AssetsRequestModels.cs | 12 +++++ src/Services/LdapService.cs | 17 +++++- 6 files changed, 139 insertions(+), 16 deletions(-) delete mode 100644 src/Models/Asset.cs create mode 100644 src/Models/AssetsModel.cs create mode 100644 src/Models/AssetsRequestModels.cs diff --git a/src/Controllers/AssetsController.cs b/src/Controllers/AssetsController.cs index 4c4a048..5e8b44f 100644 --- a/src/Controllers/AssetsController.cs +++ b/src/Controllers/AssetsController.cs @@ -1,18 +1,18 @@ using Microsoft.AspNetCore.Mvc; -using System; -using System.Threading.Tasks; using Berufsschule_HAM.Models; -using Novell.Directory.Ldap; using Berufsschule_HAM.Services; +using System.Text.Json; [Route("[controller]")] public class AssetsController : Controller { private readonly LdapService _ldap; + private readonly ILogger _logger; - public AssetsController(LdapService ldap) + public AssetsController(LdapService ldap, ILogger logger) { _ldap = ldap ?? throw new ArgumentNullException(nameof(ldap)); + _logger = logger; } [HttpGet("Index")] @@ -39,4 +39,62 @@ public class AssetsController : Controller } }); } + + [HttpPost("Update")] + public async Task Update(AssetsModifyRequestModel requestModel) + { + if (requestModel is null) + { + _logger.LogError("Unable to update an asset because the AssetsModifyRequestModel is null"); + return false; + } + string cn = requestModel.Cn; + + if (requestModel.NewCn is not null) + { + await _ldap.UpdateAsset(cn, "cn", requestModel.NewCn); + cn = requestModel.NewCn; + } + if (requestModel.Location is not null) + { + await _ldap.UpdateAsset(cn, "l", requestModel.Location); + } + if (requestModel.Name is not null) + { + await _ldap.UpdateAsset(cn, "name", requestModel.Name); + } + if (requestModel.Owner is not null) + { + await _ldap.UpdateAsset(cn, "owner", requestModel.Owner); + } + if (requestModel.SerialNumber is not null) + { + await _ldap.UpdateAsset(cn, "serialNumber", requestModel.SerialNumber); + } + if (requestModel.Description is not null) + { + AssetModel? asset = null; + if (requestModel.Description.Type is null) + { + asset ??= await _ldap.GetAssetByCnAsync(cn); + requestModel.Description.Type = asset.Description?.Type; + } + if (requestModel.Description.Purchase is not null) + { + asset ??= await _ldap.GetAssetByCnAsync(cn); + requestModel.Description.Purchase.PurchasedAt ??= asset.Description?.Purchase?.PurchasedAt; + requestModel.Description.Purchase.PurchaseDate ??= asset.Description?.Purchase?.PurchaseDate; + requestModel.Description.Purchase.PurchasedBy ??= asset.Description?.Purchase?.PurchasedBy; + requestModel.Description.Purchase.PurchaseValue ??= asset.Description?.Purchase?.PurchaseValue; + } + + if (requestModel.Description.Purchase is null) + { + asset ??= await _ldap.GetAssetByCnAsync(cn); + requestModel.Description.Purchase = asset.Description?.Purchase; + } + await _ldap.UpdateAsset(cn, "description", JsonSerializer.Serialize(requestModel.Description)); + } + return true; + } } \ No newline at end of file diff --git a/src/Exceptions/ConfigurationExceptions.cs b/src/Exceptions/ConfigurationExceptions.cs index 5ed0e25..19b3f6b 100644 --- a/src/Exceptions/ConfigurationExceptions.cs +++ b/src/Exceptions/ConfigurationExceptions.cs @@ -2,4 +2,6 @@ namespace Berufsschule_HAM.Exceptions; public class GroupModelConfigurationException : Exception { } -public class LocationModelConfigurationException : Exception { } \ No newline at end of file +public class LocationModelConfigurationException : Exception { } + +public class AssetModelConfigurationException : Exception { } \ No newline at end of file diff --git a/src/Models/Asset.cs b/src/Models/Asset.cs deleted file mode 100644 index a45be0d..0000000 --- a/src/Models/Asset.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Berufsschule_HAM.Models; -public class Asset -{ - public required string Dn { get; set; } // Full LDAP DN (readonly in many cases) - public required string CommonName { get; set; } // cn (display name) - public required string SerialNumber { get; set; } // serialNumber - public required string Description { get; set; } // description (notes) - public required string Location { get; set; } // l (location) - public required string Owner { get; set; } // owner (uid of owner) -} \ No newline at end of file diff --git a/src/Models/AssetsModel.cs b/src/Models/AssetsModel.cs new file mode 100644 index 0000000..665e0f6 --- /dev/null +++ b/src/Models/AssetsModel.cs @@ -0,0 +1,46 @@ +namespace Berufsschule_HAM.Models; + +using System.Text.Json; +using Berufsschule_HAM.Exceptions; + +public class AssetModel +{ + public required string Cn { get; set; } + public string? SerialNumber { get; set; } + public AssetDescription? Description { get; set; } + public string? Location { get; set; } + public string? Owner { get; set; } + public string? Name { get; set; } + + public AssetModel(Dictionary ldapData) + { + Cn = ldapData.GetValueOrDefault("cn") ?? throw new AssetModelConfigurationException(); + SerialNumber = ldapData.GetValueOrDefault("serialNumber"); + Location = ldapData.GetValueOrDefault("l"); + Owner = ldapData.GetValueOrDefault("owner"); + Name = ldapData.GetValueOrDefault("name"); + string? descriptionValue = ldapData.GetValueOrDefault("description"); + if (descriptionValue is null) + { + Description = new(); + } + else + { + Description = JsonSerializer.Deserialize(descriptionValue) ?? new(); + } + } +} + +public class AssetDescription +{ + public string? Type { get; set; } + public AssetPurchase? Purchase { get; set; } +} + +public class AssetPurchase +{ + public string? PurchaseDate { get; set; } + public string? PurchaseValue { get; set; } + public string? PurchasedAt { get; set; } + public string? PurchasedBy { get; set; } +} \ No newline at end of file diff --git a/src/Models/AssetsRequestModels.cs b/src/Models/AssetsRequestModels.cs new file mode 100644 index 0000000..2bdda96 --- /dev/null +++ b/src/Models/AssetsRequestModels.cs @@ -0,0 +1,12 @@ +namespace Berufsschule_HAM.Models; + +public class AssetsModifyRequestModel +{ + public required string Cn { get; set; } + public string? NewCn { get; set; } = null; + 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/Services/LdapService.cs b/src/Services/LdapService.cs index 5cdc3f0..53ac8eb 100644 --- a/src/Services/LdapService.cs +++ b/src/Services/LdapService.cs @@ -46,6 +46,7 @@ public partial class LdapService : IDisposable public string GroupsBaseDn => string.IsNullOrEmpty(_opts.GroupsOu) ? _opts.BaseDn : $"{_opts.GroupsOu},{_opts.BaseDn}"; public string MigrationsBaseDn => string.IsNullOrEmpty(_opts.MigrationsOu) ? _opts.BaseDn : $"{_opts.MigrationsOu},{_opts.BaseDn}"; public string[] UsersAttributes => ["cn", "sn", "title", "uid", "jpegPhoto", "userPassword", "description"]; + public string[] AssetsAttributes => ["CN", "description", "l", "owner", "serialNumber", "name"]; public string[] LocationsAttributes => ["cn", "l", "street", "description"]; public string[] GroupsAttributes => ["cn", "gidNumber", "description"]; public async Task>> ListLocationsAsync() @@ -148,9 +149,18 @@ public partial class LdapService : IDisposable return new LocationModel((await ListObjectBy(LocationsBaseDn, $"cn={cn}", attributes)).First()) { Cn = cn }; } + public async Task GetAssetByCnAsync(string cn) + { + return await GetAssetByCnAsync(cn, AssetsAttributes); + } + public async Task GetAssetByCnAsync(string cn, string[] attributes) + { + return new AssetModel((await ListObjectBy(AssetsBaseDn, $"cn={cn}", attributes)).First()) { Cn = cn }; + } + public async Task>> ListDeviceAsync() { - return await ListObjectBy(AssetsBaseDn, "(objectClass=device)", ["CN", "description", "l", "owner", "serialNumber"]); + return await ListObjectBy(AssetsBaseDn, "(objectClass=device)", AssetsAttributes); } public async Task CreateUser(string uid, LdapAttributeSet attributeSet) @@ -298,6 +308,11 @@ public partial class LdapService : IDisposable await UpdateObject(LocationsBaseDn, "cn", cn, attributeName, attributeValue); } + public async Task UpdateAsset(string cn, string attributeName, string attributeValue) + { + await UpdateObject(AssetsBaseDn, "cn", cn, attributeName, attributeValue); + } + public async Task UpdateObject(string baseDn, string rdnKey, string rdnValue, string attributeName, string attributeValue) { await ConnectAndBind();