Updated users LDAP spec, changed to newer LDAP package, implemented /Users/Create endpoint

This commit is contained in:
2025-09-28 19:54:45 +02:00
parent 0406f1c9f5
commit cd802eceaf
4 changed files with 64 additions and 45 deletions

View File

@@ -2,7 +2,7 @@
## users
- ObjectClass:
- extensibleObject
- inetOrgPerson
- person
- top
- cn = Name

View File

@@ -6,7 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="ElmahCore" Version="2.1.2" />
<PackageReference Include="Novell.Directory.Ldap" Version="2.2.1" />
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="4.0.0" />
<PackageReference Include="Serilog" Version="4.3.0" />
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.4" />

View File

@@ -8,7 +8,13 @@ using System.Threading.Tasks;
public class UsersController : Controller
{
private readonly LdapService _ldap;
public UsersController(LdapService ldap) => _ldap = ldap;
private readonly ILogger<UsersController> _logger;
public UsersController(LdapService ldap, ILogger<UsersController> logger)
{
_ldap = ldap;
_logger = logger;
}
[HttpGet("Index")]
public async Task<IEnumerable<Dictionary<string, string>>> Index()
@@ -35,28 +41,35 @@ public class UsersController : Controller
}
[HttpGet("Create")]
public async Task<bool> Create(string cn, string sn, string userPassword)
{
// return await new Task<bool>(() =>
// {
try
public bool Create(string cn, string sn, string? title, string? uid, string userPassword, string? description, string jpegPhoto)
{
try
{
jpegPhoto ??= System.IO.File.ReadAllText("wwwroot/user_default.jpeg"); // TODO: cleanup - make this a config setting
uid ??= sn.ToLower() + cn.ToLower();
title ??= "";
description ??= "{}";
LdapAttributeSet attributeSet = new LdapAttributeSet
{
LdapAttributeSet attributeSet = [];
attributeSet.Add(new LdapAttribute("objectClass", "organizationalPerson"));
attributeSet.Add(new LdapAttribute("objectClass", "person"));
//attributeSet.Add(new LdapAttribute("ou", "users"));
attributeSet.Add(new LdapAttribute("objectClass", "top"));
attributeSet.Add(new LdapAttribute("cn", cn));
attributeSet.Add(new LdapAttribute("sn", sn));
attributeSet.Add(new LdapAttribute("userPassword", userPassword));
_ldap.CreateUser(attributeSet);
return true;
}
catch (Exception ex)
{
return false;
}
// });
new LdapAttribute("objectClass", "inetOrgPerson"),
new LdapAttribute("objectClass", "person"),
new LdapAttribute("objectClass", "top"),
new LdapAttribute("cn", cn),
new LdapAttribute("sn", sn),
new LdapAttribute("title", title),
new LdapAttribute("uid", uid),
new LdapAttribute("jpegPhoto", jpegPhoto),
new LdapAttribute("description", description),
new LdapAttribute("userPassword", userPassword)
};
_ldap.CreateUser(uid, attributeSet);
return true;
}
catch (Exception ex)
{
_logger.LogError("Unable to create user: {ex.Message} - {ex.StackTrace}", [ex.Message, ex.StackTrace]);
return false;
}
}
}

View File

@@ -19,10 +19,10 @@ public class LdapService : IDisposable
_opts = options.Value;
_conn = new LdapConnection { SecureSocketLayer = _opts.UseSsl };
_logger = logger;
ConnectAndBind();
ConnectAndBind().Wait();
}
private void ConnectAndBind()
private async Task ConnectAndBind()
{
if (!_conn.Connected)
{
@@ -30,7 +30,7 @@ public class LdapService : IDisposable
Console.WriteLine(_opts.Port);
try
{
_conn.Connect(_opts.Host, _opts.Port);
await _conn.ConnectAsync(_opts.Host, _opts.Port);
}
catch (SystemException ex)
{
@@ -38,7 +38,7 @@ public class LdapService : IDisposable
throw;
}
}
_conn.Bind(_opts.BindDn, _opts.BindPassword);
await _conn.BindAsync(_opts.BindDn, _opts.BindPassword);
}
private string AssetsBaseDn => string.IsNullOrEmpty(_opts.AssetsOu) ? _opts.BaseDn : $"{_opts.AssetsOu},{_opts.BaseDn}";
@@ -52,8 +52,7 @@ public class LdapService : IDisposable
public async Task<IEnumerable<Dictionary<string, string>>> ListUsersAsync()
{
return await ListObjectBy(UsersBaseDn, "", ["cn", "sn", "userPassword"]);
//return await ListObjectBy("objectClass=person");
return await ListObjectBy(UsersBaseDn, "", ["cn", "sn", "title", "uid", "jpegPhoto", "userPassword", "description"]);
}
public async Task<IEnumerable<Dictionary<string, string>>> ListDeviceAsync()
@@ -61,9 +60,10 @@ public class LdapService : IDisposable
return await ListObjectBy(AssetsBaseDn, "(objectClass=device)", ["CN", "description", "l", "owner", "serialNumber"]);
}
public void CreateUser(LdapAttributeSet attributeSet)
public void CreateUser(string uid, LdapAttributeSet attributeSet)
{
CreateObject(UsersBaseDn, attributeSet);
string dn = PrependRDN($"uid={uid}", UsersBaseDn);
CreateObject(dn, attributeSet);
}
public void CreateAsset(LdapAttributeSet attributeSet)
@@ -76,27 +76,32 @@ public class LdapService : IDisposable
CreateObject(LocationsBaseDn, attributeSet);
}
private string PrependRDN(string rdn, string dn)
{
return rdn + "," + dn;
}
public async Task<IEnumerable<Dictionary<string, string>>> ListObjectBy(string baseDn, string filter, string[] attributes)
{
return await Task.Run(() =>
return await Task.Run(async () =>
{
ConnectAndBind();
var search = _conn.Search(
await ConnectAndBind();
var search = await _conn.SearchAsync(
baseDn,
LdapConnection.SCOPE_SUB,
LdapConnection.ScopeSub,
$"{filter}",
attributes,
false);
var list = new List<Dictionary<string, string>>();
while (search.hasMore())
while (await search.HasMoreAsync())
{
try
{
var e = search.next();
var attributeSet = e.getAttributeSet().ToArray();
if (attributeSet.Length == 0) { continue; }
var e = await search.NextAsync();
var attributeSet = e.GetAttributeSet();
if (attributeSet.Count == 0) { continue; }
Dictionary<string, string> attributeMap = [];
foreach (LdapAttribute attribute in attributeSet.Cast<LdapAttribute>())
foreach (LdapAttribute attribute in attributeSet)
{
attributeMap[attribute.Name] = attribute.StringValue;
}
@@ -108,15 +113,16 @@ public class LdapService : IDisposable
});
}
public void DeleteObjectByDn(string dn)
public async void DeleteObjectByDn(string dn)
{
_conn.Delete(dn);
await _conn.DeleteAsync(dn);
}
public void CreateObject(string dn, LdapAttributeSet attributeSet)
public async void CreateObject(string dn, LdapAttributeSet attributeSet)
{
await ConnectAndBind();
LdapEntry ldapEntry = new(dn, attributeSet);
_conn.Add(ldapEntry);
await _conn.AddAsync(ldapEntry);
}
public void Dispose()