Now also getting teachers from LDAP
This commit is contained in:
@@ -22,6 +22,7 @@ else
|
|||||||
<th>Classroom</th>
|
<th>Classroom</th>
|
||||||
<th>Given name</th>
|
<th>Given name</th>
|
||||||
<th>Incomplete</th>
|
<th>Incomplete</th>
|
||||||
|
<th>Role</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@@ -33,6 +34,7 @@ else
|
|||||||
<td>@user.Classroom</td>
|
<td>@user.Classroom</td>
|
||||||
<td>@user.GivenName</td>
|
<td>@user.GivenName</td>
|
||||||
<td>@user.Incomplete</td>
|
<td>@user.Incomplete</td>
|
||||||
|
<td>@user.Role</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -54,7 +56,9 @@ else
|
|||||||
|
|
||||||
private Task DownloadCsvAsync()
|
private Task DownloadCsvAsync()
|
||||||
{
|
{
|
||||||
var csv = string.Join("\r\n", users!.Select(user => string.Join(';',
|
var csv = string.Join("\r\n", users!
|
||||||
|
.Where(user => user.Role == Wollmilchsau.Models.LdapUserRole.Student)
|
||||||
|
.Select(user => string.Join(';',
|
||||||
user.GivenName ?? string.Empty,
|
user.GivenName ?? string.Empty,
|
||||||
user.Surname ?? string.Empty,
|
user.Surname ?? string.Empty,
|
||||||
user.Classroom ?? string.Empty,
|
user.Classroom ?? string.Empty,
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
namespace Wollmilchsau.Models;
|
namespace Wollmilchsau.Models;
|
||||||
|
|
||||||
|
public enum LdapUserRole
|
||||||
|
{
|
||||||
|
Student,
|
||||||
|
Teacher
|
||||||
|
}
|
||||||
|
|
||||||
public sealed record LdapUser(
|
public sealed record LdapUser(
|
||||||
string Username,
|
string Username,
|
||||||
string? Surname,
|
string? Surname,
|
||||||
string? Classroom,
|
string? Classroom,
|
||||||
string? GivenName,
|
string? GivenName,
|
||||||
bool Incomplete);
|
bool Incomplete,
|
||||||
|
LdapUserRole Role);
|
||||||
|
|||||||
@@ -24,4 +24,7 @@ public sealed class LdapOptions
|
|||||||
|
|
||||||
[Required]
|
[Required]
|
||||||
public string StudentsRole { get; init; } = string.Empty;
|
public string StudentsRole { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
[Required]
|
||||||
|
public string TeacherRole { get; init; } = string.Empty;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,27 +49,30 @@ public sealed class LdapService(IOptions<LdapOptions> options) : ILdapService
|
|||||||
private IEnumerable<LdapUser> GetUsers(LdapConnection connection)
|
private IEnumerable<LdapUser> GetUsers(LdapConnection connection)
|
||||||
{
|
{
|
||||||
var studentRoleDn = BuildDistinguishedName(_options.StudentsRole, _options.RoleBase);
|
var studentRoleDn = BuildDistinguishedName(_options.StudentsRole, _options.RoleBase);
|
||||||
|
var teacherRoleDn = BuildDistinguishedName(_options.TeacherRole, _options.RoleBase);
|
||||||
var request = new SearchRequest(
|
var request = new SearchRequest(
|
||||||
BuildDistinguishedName(_options.UserBase),
|
BuildDistinguishedName(_options.UserBase),
|
||||||
$"(memberOf={EscapeFilterValue(studentRoleDn)})",
|
$"(|(memberOf={EscapeFilterValue(studentRoleDn)})(memberOf={EscapeFilterValue(teacherRoleDn)}))",
|
||||||
SearchScope.Subtree,
|
SearchScope.Subtree,
|
||||||
"uid",
|
"uid",
|
||||||
"sn",
|
"sn",
|
||||||
"departmentNumber",
|
"departmentNumber",
|
||||||
"givenName");
|
"givenName",
|
||||||
|
"memberOf");
|
||||||
var response = (SearchResponse)connection.SendRequest(request);
|
var response = (SearchResponse)connection.SendRequest(request);
|
||||||
|
|
||||||
return response.Entries
|
return response.Entries
|
||||||
.Cast<SearchResultEntry>()
|
.Cast<SearchResultEntry>()
|
||||||
.Select(CreateUser);
|
.Select(entry => CreateUser(entry, studentRoleDn, teacherRoleDn));
|
||||||
}
|
}
|
||||||
|
|
||||||
private LdapUser CreateUser(SearchResultEntry entry)
|
private LdapUser CreateUser(SearchResultEntry entry, string studentRoleDn, string teacherRoleDn)
|
||||||
{
|
{
|
||||||
var username = GetFirstAttributeValue(entry, "uid") ?? throw new InvalidOperationException($"LDAP user entry '{entry.DistinguishedName}' is missing uid.");
|
var username = GetFirstAttributeValue(entry, "uid") ?? throw new InvalidOperationException($"LDAP user entry '{entry.DistinguishedName}' is missing uid.");
|
||||||
var surname = CapitalizeFirstLetter(GetFirstAttributeValue(entry, "sn"));
|
var surname = CapitalizeFirstLetter(GetFirstAttributeValue(entry, "sn"));
|
||||||
var classroom = GetFirstAttributeValue(entry, "departmentNumber");
|
var classroom = GetFirstAttributeValue(entry, "departmentNumber");
|
||||||
var givenName = GetFirstAttributeValue(entry, "givenName");
|
var givenName = GetFirstAttributeValue(entry, "givenName");
|
||||||
|
var role = DetermineUserRole(entry, studentRoleDn, teacherRoleDn);
|
||||||
|
|
||||||
return new LdapUser(
|
return new LdapUser(
|
||||||
username,
|
username,
|
||||||
@@ -78,7 +81,25 @@ public sealed class LdapService(IOptions<LdapOptions> options) : ILdapService
|
|||||||
givenName,
|
givenName,
|
||||||
string.IsNullOrWhiteSpace(surname) ||
|
string.IsNullOrWhiteSpace(surname) ||
|
||||||
string.IsNullOrWhiteSpace(classroom) ||
|
string.IsNullOrWhiteSpace(classroom) ||
|
||||||
string.IsNullOrWhiteSpace(givenName));
|
string.IsNullOrWhiteSpace(givenName),
|
||||||
|
role);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static LdapUserRole DetermineUserRole(SearchResultEntry entry, string studentRoleDn, string teacherRoleDn)
|
||||||
|
{
|
||||||
|
var roles = GetAttributeValues(entry, "memberOf");
|
||||||
|
|
||||||
|
if (roles.Contains(studentRoleDn, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return LdapUserRole.Student;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (roles.Contains(teacherRoleDn, StringComparer.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return LdapUserRole.Teacher;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException($"LDAP user entry '{entry.DistinguishedName}' is not a member of a supported role.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string BuildDistinguishedName(params string[] relativeNames)
|
private string BuildDistinguishedName(params string[] relativeNames)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<PackageReference Include="Hangfire" Version="1.8.24" />
|
<PackageReference Include="Hangfire" Version="1.8.24" />
|
||||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.24" />
|
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.24" />
|
||||||
<PackageReference Include="Hangfire.Redis.StackExchange" Version="1.12.0" />
|
<PackageReference Include="Hangfire.Redis.StackExchange" Version="1.12.0" />
|
||||||
<PackageReference Include="Marten.AspNetCore" Version="9.27.0" />
|
<PackageReference Include="Marten.AspNetCore" Version="9.28.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.11" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.11" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.11" />
|
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="10.0.11" />
|
||||||
<PackageReference Include="Microsoft.Graph" Version="6.5.0" />
|
<PackageReference Include="Microsoft.Graph" Version="6.5.0" />
|
||||||
|
|||||||
Reference in New Issue
Block a user