2
votes

I have an ASP.NET application which relies heavily on AD authentication. We recently broke out a service which used to live in the same solution as the application into its own solution, so that we could maintain them seperately and not impact users who rely on the service when, say, we needed to make code changes which only impacted the UI side of the application.

This has lead to an interesting issue for me though. When I debug, I have my local copy of the application pointing to a remote instance of the service, which is also used by the environment copy of the application. When I attempt to authenticate user membership using the local app -> remote service call, it fails with the following exception:

System.Runtime.InteropServices.COMException (0x8007200A): The specified directory service attribute or value does not exist.

at System.DirectoryServices.DirectoryEntry.Bind(Boolean throwIfFail) at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_SchemaEntry() at System.DirectoryServices.AccountManagement.ADStoreCtx.IsContainer(DirectoryEntry de) at System.DirectoryServices.AccountManagement.ADStoreCtx..ctor(DirectoryEntry ctxBase, Boolean ownCtxBase, String username, String password, ContextOptions options) at System.DirectoryServices.AccountManagement.PrincipalContext.CreateContextFromDirectoryEntry(DirectoryEntry entry) at System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInitNoContainer() at System.DirectoryServices.AccountManagement.PrincipalContext.DoDomainInit() at System.DirectoryServices.AccountManagement.PrincipalContext.Initialize() at System.DirectoryServices.AccountManagement.PrincipalContext.get_QueryCtx() at System.DirectoryServices.AccountManagement.Principal.FindByIdentityWithTypeHelper(PrincipalContext context, Type principalType, Nullable`1 identityType, String identityValue, DateTime refDate) at System.DirectoryServices.AccountManagement.UserPrincipal.FindByIdentity(PrincipalContext context, String identityValue) at ******************.******************.******************.IsUserMemberOfGroup(String userName, String groupName)

When I attempt to authenticate the same user in the same group, but using a web browser to hit the app on the remote box which then hits the remote service, it's happy as a clam.

The code I'm using seems trivially simple. It must be something to do with how my machine is calling out, but I'll be dipped if I can work it out.

public static bool IsUserMemberOfGroup(string userName, string groupName)
{
    try
    {
        PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "XXX");

        var user = GetUser(userName, ctx);

        if (user == null)
        {
            Log4NetLogManager.LogError("Unable to find user " + userName);
            return false;
        }

        // find the group in question
        groupName = groupName.Replace("XXX\\", string.Empty);
        GroupPrincipal group = GroupPrincipal.FindByIdentity(ctx, IdentityType.Name, groupName);

        if (group != null) return user.IsMemberOf(group);

        Log4NetLogManager.LogError("Unable to find group " + groupName);
        return false;
    }
    catch (Exception ex)
    {
        if (!ex.Message.Contains("Unknown error"))
        {
            Log4NetLogManager.LogException(string.Format("Error while checking if {0} is a member of {1}", userName, groupName), ex);
        }
        return false;
    }
}

private static UserPrincipal GetUser(string userName, PrincipalContext ctx)
{
    UserPrincipal user = null;
    userName = userName.Replace("XXX\\", string.Empty);

    try
    {
        user = UserPrincipal.FindByIdentity(ctx, userName);
    }
    catch
    {
    }

    return user ?? UserPrincipal.FindByIdentity(ctx, userName);
}
1
Did you check your .config files on both environments? Any difference?JCM
Yes--only differences are environment specific (ie., email subjects, targets, logging location, etc.).Marisa
Have you checked this: stackoverflow.com/questions/29647024/… ?JCM

1 Answers

0
votes

You're stripping the domain name too early, so you're not really trying a different second chance.

private static UserPrincipal GetUser(string userName, PrincipalContext ctx)
{
    UserPrincipal user = null;

    try
    {
        user = UserPrincipal.FindByIdentity(ctx, userName);
    }
    catch (Exception exc1)
    {
        Log4NetLogManager.LogError("First chance: " + exc1.Message);
    }
    userName = userName.Replace("XXX\\", string.Empty);

    return user ?? UserPrincipal.FindByIdentity(ctx, userName);
}

In fact I've noticed that there is a DoDomainInit in the StackTrace (either your local machine could be in a different domain or the identity string parameter could behave differently on the remote box). Remember that you have to specify user/pswd if your machine doesn't belong to the domain.

PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "XXX", "ad_usr","ad_pswd");
var user = GetUser(userName, ctx);

Also check if it can be fixed it by changing your local machine Application Pool Identity to run under the Network Service, as the error is similar to this issue.