Added Locations Create CRUD element, Updated Locations LDAP service methods

This commit is contained in:
2025-10-08 21:06:04 +02:00
parent 0ed9397280
commit ba7cde8adb
5 changed files with 82 additions and 54 deletions

View File

@@ -3,6 +3,8 @@ using Berufsschule_HAM.Models;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using System.Text.Json; using System.Text.Json;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Novell.Directory.Ldap;
using Berufsschule_HAM.Helpers;
[Authorize] [Authorize]
[Route("[controller]")] [Route("[controller]")]
@@ -24,6 +26,31 @@ public class LocationsController : Controller
return list; return list;
} }
[HttpGet("Create")]
public async Task<bool> Create(LocationsCreateRequestModel model)
{
try
{
LocationsDescription room = model.LocationsDescription;
string location = StringHelpers.Slugify(room.Location + " " + room.RoomNumber + " " + room.Seat);
LdapAttributeSet attributeSet =
[
new LdapAttribute("objectClass", "locality"),
new LdapAttribute("objectClass", "top"),
new LdapAttribute("l", location),
new LdapAttribute("description", JsonSerializer.Serialize(room))
];
await _ldap.CreateLocation(attributeSet);
return true;
}
catch (Exception ex)
{
_logger.LogError("Unable to create location: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]);
return false;
}
}
[HttpGet("Delete")] [HttpGet("Delete")]
public async Task<bool> Delete(string cn) public async Task<bool> Delete(string cn)
{ {
@@ -49,37 +76,20 @@ public class LocationsController : Controller
_logger.LogError("Unable to update a location because the LocationsModifyRequestModel is null"); _logger.LogError("Unable to update a location because the LocationsModifyRequestModel is null");
return false; return false;
} }
string cn = requestModel.Cn; try
{
string location = requestModel.Location;
LocationsDescription room = requestModel.Description;
string newLocation = StringHelpers.Slugify(room.Location + " " + room.RoomNumber + " " + room.Seat); // TODO: fix DRY violation
if (requestModel.NewCn is not null) await _ldap.UpdateLocation(location, "description", JsonSerializer.Serialize(room));
{ await _ldap.UpdateLocation(location, "l", newLocation);
await _ldap.UpdateLocation(cn, "cn", requestModel.NewCn);
cn = requestModel.NewCn;
}
if (requestModel.Location is not null)
{
await _ldap.UpdateLocation(cn, "location", requestModel.Location);
}
if (requestModel.Street is not null)
{
await _ldap.UpdateLocation(cn, "street", requestModel.Street);
}
if (requestModel.Description is not null)
{
LocationsDescription description = requestModel.Description;
LocationModel? location = null;
if (description.Seat is null)
{
location ??= await _ldap.GetLocationByCnAsync(cn);
description.Seat = location.Description?.Seat;
}
else if (description.RoomNumber is null)
{
location ??= await _ldap.GetLocationByCnAsync(cn);
description.RoomNumber = location.Description?.RoomNumber;
}
await _ldap.UpdateLocation(cn, "description", JsonSerializer.Serialize(requestModel.Description));
}
return true; return true;
} }
catch (Exception ex)
{
_logger.LogError("Unable to update location: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]);
return false;
}
}
} }

View File

@@ -0,0 +1,19 @@
using System.Text.RegularExpressions;
namespace Berufsschule_HAM.Helpers;
public static class StringHelpers
{
public static string Slugify(string input)
{
if (string.IsNullOrWhiteSpace(input))
{
return string.Empty;
}
input = input.ToLowerInvariant();
input = Regex.Replace(input, @"[^a-z0-9\s-]", "", RegexOptions.None, TimeSpan.FromMilliseconds(100));
input = Regex.Replace(input, @"[\s-]+", "-", RegexOptions.None, TimeSpan.FromMilliseconds(100));
input = input.Trim('-');
return input;
}
}

View File

@@ -4,15 +4,11 @@ using System.Text.Json;
using Berufsschule_HAM.Exceptions; using Berufsschule_HAM.Exceptions;
public class LocationModel public class LocationModel
{ {
public required string Cn { get; set; } public required string Location { get; set; }
public LocationsDescription? Description { get; set; } public LocationsDescription? Description { get; set; }
public string? Location { get; set; }
public string? Street { get; set; }
public LocationModel(Dictionary<string, string> ldapData) public LocationModel(Dictionary<string, string> ldapData)
{ {
Cn = ldapData.GetValueOrDefault("cn") ?? throw new LocationModelConfigurationException(); Location = ldapData.GetValueOrDefault("l") ?? throw new LocationModelConfigurationException();
Location = ldapData.GetValueOrDefault("l");
Street = ldapData.GetValueOrDefault("street");
string? descriptionValue = ldapData.GetValueOrDefault("description"); string? descriptionValue = ldapData.GetValueOrDefault("description");
if (descriptionValue is null) if (descriptionValue is null)
{ {
@@ -26,6 +22,7 @@ public class LocationModel
} }
public class LocationsDescription public class LocationsDescription
{ {
public string? Location { get; set; }
public string? RoomNumber { get; set; } public string? RoomNumber { get; set; }
public string? Seat { get; set; } public string? Seat { get; set; }
} }

View File

@@ -1,10 +1,12 @@
namespace Berufsschule_HAM.Models; namespace Berufsschule_HAM.Models;
public class LocationsCreateRequestModel
{
public required LocationsDescription LocationsDescription { get; set; }
}
public class LocationsModifyRequestModel public class LocationsModifyRequestModel
{ {
public required string Cn { get; set; } public required string Location { get; set; }
public string? NewCn { get; set; } = null; public required LocationsDescription Description { get; set; }
public LocationsDescription? Description { get; set; } = null;
public string? Location { get; set; } = null;
public string? Street { get; set; } = null;
} }

View File

@@ -47,13 +47,13 @@ public partial class LdapService : IDisposable
public string MigrationsBaseDn => string.IsNullOrEmpty(_opts.MigrationsOu) ? _opts.BaseDn : $"{_opts.MigrationsOu},{_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[] UsersAttributes => ["cn", "sn", "title", "uid", "jpegPhoto", "userPassword", "description"];
public string[] AssetsAttributes => ["CN", "description", "l", "owner", "serialNumber", "name"]; public string[] AssetsAttributes => ["CN", "description", "l", "owner", "serialNumber", "name"];
public string[] LocationsAttributes => ["cn", "l", "street", "description"]; public string[] LocationsAttributes => ["l", "description"];
public string[] GroupsAttributes => ["cn", "gidNumber", "description"]; public string[] GroupsAttributes => ["cn", "gidNumber", "description"];
public async Task<IEnumerable<LocationModel>> ListLocationsAsync() public async Task<IEnumerable<LocationModel>> ListLocationsAsync()
{ {
IEnumerable<Dictionary<string, string>> locations = await ListObjectBy(LocationsBaseDn, "", LocationsAttributes); IEnumerable<Dictionary<string, string>> locations = await ListObjectBy(LocationsBaseDn, "", LocationsAttributes);
List<LocationModel> models = []; List<LocationModel> models = [];
locations.ToList().ForEach(x => models.Add(new LocationModel(x) {Cn = x["cn"]})); locations.ToList().ForEach(x => models.Add(new LocationModel(x) {Location = x[LocationsAttributes[0]]}));
return models; return models;
} }
@@ -150,9 +150,9 @@ public partial class LdapService : IDisposable
{ {
return await GetLocationByCnAsync(cn, LocationsAttributes); return await GetLocationByCnAsync(cn, LocationsAttributes);
} }
public async Task<LocationModel> GetLocationByCnAsync(string cn, string[] attributes) public async Task<LocationModel> GetLocationByCnAsync(string location, string[] attributes)
{ {
return new LocationModel((await ListObjectBy(LocationsBaseDn, $"cn={cn}", attributes)).First()) { Cn = cn }; return new LocationModel((await ListObjectBy(LocationsBaseDn, $"l={location}", attributes)).First()) { Location = location };
} }
public async Task<AssetModel> GetAssetByCnAsync(string cn) public async Task<AssetModel> GetAssetByCnAsync(string cn)
@@ -197,12 +197,12 @@ public async Task CreateAsset(LdapAttributeSet attributeSet)
public async Task CreateLocation(LdapAttributeSet attributeSet) public async Task CreateLocation(LdapAttributeSet attributeSet)
{ {
string? cn = attributeSet.GetAttribute("cn")?.StringValue; string? cn = attributeSet.GetAttribute("l")?.StringValue;
if (string.IsNullOrEmpty(cn)) if (string.IsNullOrEmpty(cn))
throw new ArgumentException("AttributeSet must contain a cn attribute."); throw new ArgumentException("AttributeSet must contain an l attribute.");
string dn = PrependRDN($"cn={cn}", LocationsBaseDn); string dn = PrependRDN($"l={cn}", LocationsBaseDn);
await CreateObject(dn, attributeSet); await CreateObject(dn, attributeSet);
} }
@@ -302,9 +302,9 @@ public async Task CreateAsset(LdapAttributeSet attributeSet)
await DeleteObjectByDnAsync(dn); await DeleteObjectByDnAsync(dn);
} }
public async Task DeleteLocationAsync(string cn) public async Task DeleteLocationAsync(string location)
{ {
string dn = PrependRDN($"cn={cn}", LocationsBaseDn); string dn = PrependRDN($"l={location}", LocationsBaseDn);
await DeleteObjectByDnAsync(dn); await DeleteObjectByDnAsync(dn);
} }
@@ -324,9 +324,9 @@ public async Task CreateAsset(LdapAttributeSet attributeSet)
await UpdateObject(GroupsBaseDn, "cn", cn, attributeName, attributeValue); await UpdateObject(GroupsBaseDn, "cn", cn, attributeName, attributeValue);
} }
public async Task UpdateLocation(string cn, string attributeName, string attributeValue) public async Task UpdateLocation(string location, string attributeName, string attributeValue)
{ {
await UpdateObject(LocationsBaseDn, "cn", cn, attributeName, attributeValue); await UpdateObject(LocationsBaseDn, "l", location, attributeName, attributeValue);
} }
public async Task UpdateAsset(string cn, string attributeName, string attributeValue) public async Task UpdateAsset(string cn, string attributeName, string attributeValue)