1

Is there any way possible to access a clients AD users and groups outsite an intranet setting?

I am not looking for azure solutions.

The tech i am working with is .Net Core web api and Angular as frontend.

Retrieving AD information on my own domain is achievable and i could get UserPrincipals, but if the web API and AD is not hosted on the same server, how does that work?

pontusv
  • 283
  • 1
  • 13
  • 1
    For retrieving ad info you need to have an authenticated user associated with the connection you are making. And by default querying is not allowed to anonymous users and you are already aware of System.DirectoryServices nuget package. – Eldar Nov 15 '19 at 18:08
  • 1
    When you say "get UserPrincipals", does that mean you are using Windows Authentication, or are you manually looking up an account using, for example, `PrincipalSearcher` which would return a `UserPrincipal`? – Gabriel Luci Nov 15 '19 at 18:37
  • @Eldar Could you point me in any direction regarding authenticating the user? Yes the System.DirectoryServices is what i plan on using – pontusv Nov 15 '19 at 23:14
  • @GabrielLuci I have not built the application yet, but this functionality is crucial to it. So i am researching the possibility beforehand. I would like to get the clients AD groups on his/her domain (maybe the user authenticates on visiting the site) – pontusv Nov 15 '19 at 23:17
  • If this is in an intranet setting, where users log into their computer with an AD account that they would log into your site with, then you can implement [Windows Authentication](https://learn.microsoft.com/en-us/aspnet/core/security/authentication/windowsauth) – Gabriel Luci Nov 16 '19 at 00:00
  • @GabrielLuci This will not be in an intranet settings unfortunately, maybe i am looking for something that is not possible? – pontusv Nov 16 '19 at 18:56

2 Answers2

1

I can't tell you exactly how to do it, since I haven't done it with .NET Core yet, but I can tell you what you need to do and you can look up more details about each part.

  1. Use forms authentication. You will need a login page that asks for their username and password.
  2. Validate the credentials. There are several ways to do this. My favourite is in this answer, which uses LdapConnection from System.DirectoryServices.Protocols because it's the least amount of network requests needed to do the job and it will tell your why credentials fail (which would let you take the user to a "change password" page if their password has expired, for example). However, using DirectoryEntry/DirectorySearcher is easier for looking up groups, so you might want to also use that for validating too, by using the user's credentials in the constructor of DirectoryEntry (but you'd lose knowing the reason for failed attempts).
  3. Look up the user's account. I prefer using DirectoryEntry/DirectorySearcher from System.DirectoryServices for this. Eldar's answer shows how to do that.
  4. Find the user's groups. I wrote a whole article about this: Finding all of a user's groups. Assuming you only have one domain in your environment, and you already have a DirectoryEntry object for the user's account, this code will work:
private static IEnumerable<string> GetUserMemberOf(DirectoryEntry de) {
    var groups = new List<string>();

    //retrieve only the memberOf attribute from the user
    de.RefreshCache(new[] {"memberOf"});

    while (true) {
        var memberOf = de.Properties["memberOf"];
        foreach (string group in memberOf) {
            var groupDe = new DirectoryEntry($"LDAP://{group.Replace("/", "\\/")}");
            groupDe.RefreshCache(new[] {"cn"});
            groups.Add(groupDe.Properties["cn"].Value as string);
        }

        //AD only gives us 1000 or 1500 at a time (depending on the server version)
        //so if we've hit that, go see if there are more
        if (memberOf.Count != 1500 && memberOf.Count != 1000) break;

        try {
            de.RefreshCache(new[] {$"memberOf;range={groups.Count}-*"});
        } catch (COMException e) {
            if (e.ErrorCode == unchecked((int) 0x80072020)) break; //no more results

            throw;
        }
    }
    return groups;
}

If you have more than one domain in your environment, then it'll be a bit more complicated.

Gabriel Luci
  • 38,328
  • 4
  • 55
  • 84
  • Incredible, and is this possible if the server/website is hosted outside of the clients network? For example if i had 2 users that are located in different citys with their own domains with local AD, i could get their AD group data for each of them? – pontusv Nov 17 '19 at 15:16
  • 1
    Yes. You would just have to make sure there is no firewall blocking access. You specify the server in the LDAP string, like `LDAP://example.com`. By default it will use port 389, but if you want to use encryption, then you have to specify port 636: `LDAP://example.com:636` (and you also have to trust the certificate they use). – Gabriel Luci Nov 17 '19 at 15:44
0
       var entry = new DirectoryEntry("LDAP://DC=DomainController,DC=com","UserName","P4$$w0Rd!???"); 
       // userName password must be valid

        var searcher = new DirectorySearcher(entry);
        searcher.PropertiesToLoad.Add("sn");
        var accName = "accNameToSearch"; // you can also use wildcart 
        // https://learn.microsoft.com/en-us/windows/win32/adsi/search-filter-syntax
        searcher.Filter = $"(&(objectCategory=person)(objectClass=user)(sAMAccountName={accName}))"; 
        var result = searcher.FindOne();
        var sn = result.Properties["sn"];

There is no UserPrincipal class for that nuget package yet. But you can still query users and other stuff with ad query syntax like above.

Eldar
  • 9,781
  • 2
  • 10
  • 35
  • And how would i get the username and password from a client visiting the site? Is this DirectorySearcher possible outside an intranet setting? – pontusv Nov 16 '19 at 18:58