mirror of
https://github.com/LD-Reborn/Berufsschule_HAM.git
synced 2025-12-20 06:51:55 +00:00
Merge pull request #119 from LD-Reborn/76-feature-implement-a-counter-for-the-assetid
76 feature implement a counter for the assetid
This commit is contained in:
@@ -28,6 +28,10 @@ public class AssetsController : Controller
|
|||||||
{
|
{
|
||||||
var assetList = await _ldap.ListDeviceAsync(Cn);
|
var assetList = await _ldap.ListDeviceAsync(Cn);
|
||||||
result = new AssetsGetResponseModel(successful: true, assetModel: assetList);
|
result = new AssetsGetResponseModel(successful: true, assetModel: assetList);
|
||||||
|
if (result.AssetsModel is not null)
|
||||||
|
{
|
||||||
|
result.AssetsModel.Owner = result.AssetsModel?.Owner?.Replace("uid=", "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -45,6 +49,11 @@ public class AssetsController : Controller
|
|||||||
{
|
{
|
||||||
var assetList = await _ldap.ListDeviceAsync();
|
var assetList = await _ldap.ListDeviceAsync();
|
||||||
result = new AssetsGetAllResponseModel(successful: true, assetsModel: assetList);
|
result = new AssetsGetAllResponseModel(successful: true, assetsModel: assetList);
|
||||||
|
result.AssetsModel = result.AssetsModel?.Select(asset =>
|
||||||
|
{
|
||||||
|
asset.Owner = asset.Owner?.Replace("uid=", "");
|
||||||
|
return asset;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -71,13 +80,10 @@ public class AssetsController : Controller
|
|||||||
{
|
{
|
||||||
LdapAttributeSet attributeSet =
|
LdapAttributeSet attributeSet =
|
||||||
[
|
[
|
||||||
new LdapAttribute("objectClass", new[] {"top", "device", "extensibleObject"}),
|
new LdapAttribute("objectClass", ["top", "device", "extensibleObject"]),
|
||||||
];
|
];
|
||||||
|
|
||||||
if (assetModel.Cn != null)
|
attributeSet.Add(new LdapAttribute("cn", await _ldap.GetAssetCounterAndIncrementAsync()));
|
||||||
{
|
|
||||||
attributeSet.Add(new LdapAttribute("cn", assetModel.Cn));
|
|
||||||
}
|
|
||||||
if (assetModel.SerialNumber != null)
|
if (assetModel.SerialNumber != null)
|
||||||
{
|
{
|
||||||
attributeSet.Add(new LdapAttribute("serialNumber", assetModel.SerialNumber));
|
attributeSet.Add(new LdapAttribute("serialNumber", assetModel.SerialNumber));
|
||||||
@@ -168,7 +174,7 @@ public class AssetsController : Controller
|
|||||||
}
|
}
|
||||||
if (requestModel.Owner is not null)
|
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)
|
if (requestModel.SerialNumber is not 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;
|
|
||||||
}
|
|
||||||
@@ -70,4 +70,9 @@ public class AssetsTableViewModel
|
|||||||
public required string AssetCn { get; set; }
|
public required string AssetCn { get; set; }
|
||||||
public string? AssetName { get; set; }
|
public string? AssetName { get; set; }
|
||||||
public string? LocationName { get; set; }
|
public string? LocationName { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AssetsMetadataModel
|
||||||
|
{
|
||||||
|
public int CnCounter { get; set; }
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,31 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
namespace Berufsschule_HAM.Models;
|
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 AssetDescription? Description { get; set; } = null;
|
||||||
public string? Location { get; set; } = null;
|
public string? Location { get; set; } = null;
|
||||||
public string? Name { get; set; } = null;
|
public string? Name { get; set; } = null;
|
||||||
public string? Owner { get; set; } = null;
|
public string? Owner { get; set; } = null;
|
||||||
public string? SerialNumber { 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;
|
||||||
}
|
}
|
||||||
@@ -32,7 +32,7 @@ builder.Services.AddElmah<XmlFileErrorLog>(Options =>
|
|||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddSingleton<LdapService>();
|
builder.Services.AddSingleton<LdapService>();
|
||||||
builder.Services.AddSingleton<MigrationService>();
|
builder.Services.AddHostedService<MigrationService>();
|
||||||
|
|
||||||
builder.Services.AddHealthChecks()
|
builder.Services.AddHealthChecks()
|
||||||
.AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck");
|
.AddCheck<DatabaseHealthCheck>("DatabaseHealthCheck");
|
||||||
@@ -123,8 +123,5 @@ app.MapControllerRoute(
|
|||||||
app.MapHealthChecks("/healthz")
|
app.MapHealthChecks("/healthz")
|
||||||
.RequireAuthorization();
|
.RequireAuthorization();
|
||||||
|
|
||||||
// Run migrations
|
|
||||||
using var scope = app.Services.CreateScope();
|
|
||||||
var migrationService = scope.ServiceProvider.GetRequiredService<MigrationService>();
|
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -118,6 +118,51 @@ public partial class LdapService : IDisposable
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string> GetAssetCounterAndIncrementAsync()
|
||||||
|
{
|
||||||
|
AssetsMetadataModel assetModel = await GetAssetCounterAsync();
|
||||||
|
string returnValue = assetModel.CnCounter.ToString();
|
||||||
|
assetModel.CnCounter++;
|
||||||
|
await UpdateAssetCounterAsync(assetModel);
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<AssetsMetadataModel> GetAssetCounterAsync()
|
||||||
|
{
|
||||||
|
Dictionary<string, string> entry = (await ListObjectBy(_opts.BaseDn, _opts.AssetsOu, ["description"])).First();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string description = entry["description"];
|
||||||
|
return JsonSerializer.Deserialize<AssetsMetadataModel>(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<bool> 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<IEnumerable<UserModel>> ListUsersAsync(string[] attributes)
|
public async Task<IEnumerable<UserModel>> ListUsersAsync(string[] attributes)
|
||||||
{
|
{
|
||||||
List<UserModel> returnValue = [];
|
List<UserModel> returnValue = [];
|
||||||
@@ -396,6 +441,11 @@ public async Task CreateAsset(LdapAttributeSet attributeSet)
|
|||||||
await _conn.AddAsync(ldapEntry);
|
await _conn.AddAsync(ldapEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task ModifyAsync(string dn, LdapModification ldapModification)
|
||||||
|
{
|
||||||
|
await _conn.ModifyAsync(dn, ldapModification);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_conn != null && _conn.Connected)
|
if (_conn != null && _conn.Connected)
|
||||||
|
|||||||
@@ -2,9 +2,10 @@ using Berufsschule_HAM.Models;
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Novell.Directory.Ldap;
|
using Novell.Directory.Ldap;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Text.Json;
|
||||||
namespace Berufsschule_HAM.Services;
|
namespace Berufsschule_HAM.Services;
|
||||||
|
|
||||||
public class MigrationService
|
public class MigrationService : IHostedService
|
||||||
{
|
{
|
||||||
private readonly LdapService _ldapService;
|
private readonly LdapService _ldapService;
|
||||||
private readonly ILogger<MigrationService> _logger;
|
private readonly ILogger<MigrationService> _logger;
|
||||||
@@ -15,8 +16,15 @@ public class MigrationService
|
|||||||
_ldapService = ldapService;
|
_ldapService = ldapService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_ldapConfig = ldapConfig.Value;
|
_ldapConfig = ldapConfig.Value;
|
||||||
MigrateAsync().ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task StartAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
await MigrateAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task StopAsync(CancellationToken cancellationToken) { }
|
||||||
|
|
||||||
public async Task<MigrationModel> MigrateAsync()
|
public async Task<MigrationModel> MigrateAsync()
|
||||||
{
|
{
|
||||||
MigrationModel migrationModel = await _ldapService.GetMigrationVersionAsync();
|
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]);
|
_logger.LogInformation("Migrating LDAP database from version {version} to {methodVersion}", [version, methodVersion + 1]);
|
||||||
if (method is null) { continue; }
|
if (method is null) { continue; }
|
||||||
#pragma warning disable CS8605 // Unboxing a possibly null value.
|
Task<int>? invocation = (Task<int>?)method.Invoke(null, [_ldapService]) ?? throw new Exception("Invocation is null");
|
||||||
version = (int)method.Invoke(null, [_ldapService]);
|
version = await invocation;
|
||||||
#pragma warning restore CS8605 // Unboxing a possibly null value.
|
migrationModel.Version = version;
|
||||||
|
await _ldapService.UpdateMigrationVersionAsync(migrationModel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version != migrationModel.Version)
|
|
||||||
{
|
|
||||||
migrationModel.Version = version;
|
|
||||||
await _ldapService.UpdateMigrationVersionAsync(migrationModel);
|
|
||||||
}
|
|
||||||
|
|
||||||
return migrationModel;
|
return migrationModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +95,15 @@ public class MigrationService
|
|||||||
await TryCreateObjectIgnoreAlreadyExists(ldapService, ldapService.MigrationsBaseDn, migrationsAttributes);
|
await TryCreateObjectIgnoreAlreadyExists(ldapService, ldapService.MigrationsBaseDn, migrationsAttributes);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
public static async Task<int> 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)
|
private static async Task TryCreateObjectIgnoreAlreadyExists(LdapService ldapService, string dn, LdapAttributeSet ldapAttributes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -182,10 +182,6 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<!-- Basic Info -->
|
<!-- Basic Info -->
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">@T["Asset ID (Cn)"] *</label>
|
|
||||||
<input type="text" class="form-control" name="Cn" required />
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">@T["Name"]</label>
|
<label class="form-label">@T["Name"]</label>
|
||||||
<input type="text" class="form-control" name="Name" />
|
<input type="text" class="form-control" name="Name" />
|
||||||
@@ -232,7 +228,7 @@
|
|||||||
<!-- Dynamic attribute rows will appear here -->
|
<!-- Dynamic attribute rows will appear here -->
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-sm btn-success mt-3" id="addAttributeBtn">
|
<button type="button" class="btn btn-sm btn-success mt-3" id="addAttributeBtn">
|
||||||
➕ @T["Add Attribute"]
|
@T["Add Attribute"]
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -363,10 +359,6 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div class="row g-3">
|
<div class="row g-3">
|
||||||
<!-- Same fields as in Create -->
|
<!-- Same fields as in Create -->
|
||||||
<div class="col-md-6">
|
|
||||||
<label class="form-label">@T["Asset ID (Cn)"] *</label>
|
|
||||||
<input type="text" class="form-control" name="Cn" readonly />
|
|
||||||
</div>
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label class="form-label">@T["Name"]</label>
|
<label class="form-label">@T["Name"]</label>
|
||||||
<input type="text" class="form-control" name="Name" />
|
<input type="text" class="form-control" name="Name" />
|
||||||
@@ -408,7 +400,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="updateAttributesContainer" class="d-flex flex-column gap-2"></div>
|
<div id="updateAttributesContainer" class="d-flex flex-column gap-2"></div>
|
||||||
<button type="button" class="btn btn-sm btn-success mt-3" id="updateAddAttributeBtn">
|
<button type="button" class="btn btn-sm btn-success mt-3" id="updateAddAttributeBtn">
|
||||||
➕ @T["Add Attribute"]
|
@T["Add Attribute"]
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -450,6 +442,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const updateForm = document.getElementById('updateAssetForm');
|
const updateForm = document.getElementById('updateAssetForm');
|
||||||
const updateAttributesContainer = document.getElementById('updateAttributesContainer');
|
const updateAttributesContainer = document.getElementById('updateAttributesContainer');
|
||||||
const addAttrBtn = document.getElementById('updateAddAttributeBtn');
|
const addAttrBtn = document.getElementById('updateAddAttributeBtn');
|
||||||
|
let assetId = null;
|
||||||
|
|
||||||
addAttrBtn.addEventListener('click', () => {
|
addAttrBtn.addEventListener('click', () => {
|
||||||
const row = document.createElement('div');
|
const row = document.createElement('div');
|
||||||
@@ -470,7 +463,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
updateModal.addEventListener('show.bs.modal', async event => {
|
updateModal.addEventListener('show.bs.modal', async event => {
|
||||||
const button = event.relatedTarget;
|
const button = event.relatedTarget;
|
||||||
const assetId = button.getAttribute('data-asset-id');
|
assetId = button.getAttribute('data-asset-id');
|
||||||
|
|
||||||
updateAttributesContainer.innerHTML = '';
|
updateAttributesContainer.innerHTML = '';
|
||||||
updateForm.reset();
|
updateForm.reset();
|
||||||
@@ -524,7 +517,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const formData = new FormData(updateForm);
|
const formData = new FormData(updateForm);
|
||||||
const jsonData = {};
|
const jsonData = {"Cn": assetId};
|
||||||
for (const [key, value] of formData.entries()) {
|
for (const [key, value] of formData.entries()) {
|
||||||
if (!value) continue;
|
if (!value) continue;
|
||||||
const keys = key.split('.');
|
const keys = key.split('.');
|
||||||
|
|||||||
Reference in New Issue
Block a user