Læringsmål

  • Kan forklare, hvad Active Directory og LDAP bruges til i en enterprise-kontekst
  • Kan oprette en LDAP-forbindelse fra et C#-program via NuGet-pakker
  • Kan hente og modellere data fra AD (brugere og grupper)
  • Kan søge i AD baseret på kriterier som navn, email eller afdeling
  • Kan håndtere fejl og manglende data ved kommunikation med eksterne systemer

LDAP og Active Directory

Virksomheden har et Active Directory (AD), og I skal bygge en C#-platform, der hurtigt kan:

  1. Hente en liste af alle grupper
  2. Hente en liste af alle brugere
  3. Hente en kombineret liste af grupper og deres medlemmer
  4. Søge efter specifikke brugere eller grupper (navn, email, afdeling osv.)

LDAP er protokollen, C#-programmer bruger til at læse data fra AD.

Active Directory i enterprise

Active Directory er Microsofts directory service — en central database over:

  • Brugere — navn, email, afdeling, login
  • Grupper — fx "IT-afdeling", "GF2-elever"
  • Computere — maskiner i domænet
  • Rettigheder — hvem må tilgå hvilke ressourcer

I stedet for separate login på hver app logger medarbejdere ind én gang (Single Sign-On) og får adgang baseret på deres AD-konto. Det er standard i danske virksomheder, kommuner og skoler.

Directory = struktureret opslagsdatabase. Tænk på det som en telefonbog, hvor du kan slå op på navn, afdeling eller email.

Test-miljø — fælles AD-reader

Indtil I har jeres eget AD i luften, kan I teste med denne fælles læsebruger:

private static string _server = "10.133.71.100";
private static string _username = "adReader";
private static string _password = "Merc1234!";
private static string _domain = "mags.local";

Test-credentials må kun bruges i undervisningsmiljøet. Commit aldrig rigtige adgangskoder til GitHub — brug appsettings.json, miljøvariabler eller User Secrets i rigtige projekter.

NuGet og System.DirectoryServices.Protocols

Når vi programmerer mod MS AD, bruger vi NuGet som package manager. Hent pakken:

System.DirectoryServices.Protocols på NuGet

Dokumentation:

LdapConnection (Microsoft Docs)

dotnet add package System.DirectoryServices.Protocols
using System.DirectoryServices.Protocols;
using System.Net;

Opret forbindelse til AD

private static LdapConnection Connect()
{
    var credential = new NetworkCredential($"{_username}@{_domain}", _password);
    var connection = new LdapConnection(_server)
    {
        Credential = credential,
        AuthType = AuthType.Negotiate
    };

    connection.Bind();   // Test forbindelsen — kaster fejl hvis login fejler
    return connection;
}

Bind = log ind på AD. Kaldes altid før søgninger.

Hent alle grupper

public static List<ADGroup> GetAllGroups()
{
    var groups = new List<ADGroup>();

    using (var connection = Connect())
    {
        // Søg i domænet mags.local efter alle objekter af typen "group"
        var searchRequest = new SearchRequest(
            "DC=mags,DC=local",
            "(objectClass=group)",
            SearchScope.Subtree,
            "cn",
            "description"
        );

        try
        {
            var response = (SearchResponse)connection.SendRequest(searchRequest);

            foreach (SearchResultEntry groupEntry in response.Entries)
            {
                var group = new ADGroup
                {
                    Name = groupEntry.Attributes["cn"]?[0]?.ToString() ?? "N/A",
                    Description = groupEntry.Attributes["description"]?[0]?.ToString() ?? "N/A"
                };
                groups.Add(group);
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Der skete en fejl ved hentning af grupper: {ex.Message}");
        }
    }

    return groups;
}

public class ADGroup
{
    public string Name { get; set; } = "";
    public string Description { get; set; } = "";
}

LDAP-filter: (objectClass=group) finder alle grupper. SearchScope.Subtree søger i hele domænet.

Hent alle brugere

public static List<ADUser> GetAllUsers()
{
    var users = new List<ADUser>();

    using (var connection = Connect())
    {
        var searchRequest = new SearchRequest(
            "DC=mags,DC=local",
            "(objectClass=user)",
            SearchScope.Subtree,
            "cn",
            "samAccountName",
            "mail",
            "department",
            "title",
            "distinguishedName"
        );

        try
        {
            var response = (SearchResponse)connection.SendRequest(searchRequest);

            foreach (SearchResultEntry entry in response.Entries)
            {
                var user = new ADUser
                {
                    Name = entry.Attributes["cn"]?[0]?.ToString() ?? "N/A",
                    Username = entry.Attributes["samAccountName"]?[0]?.ToString() ?? "N/A",
                    Email = entry.Attributes["mail"]?[0]?.ToString() ?? "N/A",
                    Department = entry.Attributes["department"]?[0]?.ToString() ?? "N/A",
                    Title = entry.Attributes["title"]?[0]?.ToString() ?? "N/A",
                    DistinguishedName = entry.Attributes["distinguishedName"]?[0]?.ToString() ?? "N/A"
                };
                users.Add(user);
            }
        }
        catch (Exception ex)
        {
            throw new Exception($"Fejl ved hentning af brugere: {ex.Message}");
        }
    }

    return users;
}

public class ADUser
{
    public string Name { get; set; } = "";
    public string Username { get; set; } = "";
    public string Email { get; set; } = "";
    public string Department { get; set; } = "";
    public string Title { get; set; } = "";
    public string DistinguishedName { get; set; } = "";
}

Map AD-attributter til C#-klasser — så resten af programmet arbejder med typed objekter, ikke rå LDAP-strenge. Brug ?? "N/A" når attributten mangler.

Grupper og medlemmer (kombineret liste)

For at hente hvem der er medlem af hvilken gruppe, søg grupper med attributten member — eller søg brugerens memberOf:

// Eksempel-filter for én gruppe med medlemmer
var searchRequest = new SearchRequest(
    "DC=mags,DC=local",
    "(cn=IT-Gruppe)",
    SearchScope.Subtree,
    "cn", "member"
);

// member indeholder Distinguished Names for hver bruger i gruppen

Byg en Dictionary<string, List<string>> eller en klasse ADGroupWithMembers der kombinerer gruppenavn med liste af medlemmer.

Søgning med LDAP-filter

LDAP-filtre følger en specifik syntaks — brug dem til krav #4:

(name=Ada*)                                    — navn starter med Ada
(mail=*@mags.local)                            — email-domæne
(department=IT)                                — afdeling
(&(objectClass=user)(department=IT))           — bruger OG afdeling IT
(|(department=IT)(department=Support))         — IT ELLER Support

I C# bygger du filter-strengen dynamisk:

string filter = $"(&(objectClass=user)(mail={email}))";
var searchRequest = new SearchRequest("DC=mags,DC=local", filter, SearchScope.Subtree, "cn", "mail");

Start med én attribut ad gangen — udvid derefter.

Fejlhåndtering og robusthed

Kommunikation med AD kræver defensiv programmering:

  • Wrap søgninger i try/catch
  • Tjek attributter med ?. og fallback (?? "N/A")
  • Håndter timeout og manglende forbindelse
  • Log fejl — vis brugervenlig besked i UI
try
{
    var groups = GetAllGroups();
    foreach (var group in groups)
        Console.WriteLine($"{group.Name}: {group.Description}");
}
catch (Exception ex)
{
    Console.WriteLine($"AD-fejl: {ex.Message}");
}

Dette emne forbereder projektforløbet Programmering i et enterprise, hvor I arbejder med rigtige enterprise-scenarier mod Active Directory.

Opsummering

  • AD er virksomhedens centrale bruger- og gruppedatabase
  • C# forbinder via NuGet-pakken System.DirectoryServices.Protocols
  • BindSearch → map resultater til C#-klasser
  • Hent grupper med (objectClass=group), brugere med (objectClass=user)
  • Brug LDAP-filtre til søgning på navn, email og afdeling
  • Håndter altid fejl og manglende attributter

Test din viden

Spørgsmål 1

Hvad bruges LDAP til i enterprise-sammenhæng?

Spørgsmål 2

Hvilken NuGet-pakke bruges til AD-forbindelse i kapitlet?

Spørgsmål 3

Hvad gør connection.Bind()?

Spørgsmål 4

Hvilket LDAP-filter henter alle grupper i eksemplet?

Spørgsmål 5

Hvorfor bruges ?? "N/A" ved mapping af AD-attributter?

Spørgsmål 6

Hvad betyder SearchScope.Subtree?

Spørgsmål 7

Hvad er Single Sign-On (SSO) i relation til AD?