I've read plenty of similar StackOverflow questions but none seem to address the issue I'm seeing. If I query for a user using userprincipalname I get back a search result with exactly 34 properties. None of the custom properties are returned. If I query again using a custom property like employeeNumber I get back a result with 71 properties. All custom properties are included.
My issue is that I don't have the employeeNumber at run time, just the userprincipalname. I need to get back all of the custom properties all of the time. Hopefully that makes sense. Here's my practice code:
string sid = "";
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
UserPrincipal user = UserPrincipal.Current;
//sid = user.SamAccountName;
sid = user.UserPrincipalName;
//sid = user.Sid.ToString();
DirectoryEntry entry = user.GetUnderlyingObject() as DirectoryEntry;
if (entry.Properties.Contains("employeeNumber"))
{
//this doesn't work
}
}
DirectoryEntry ldapConnection = new DirectoryEntry("companyname.com");
ldapConnection.Path = "LDAP://DC=companyname,DC=com";
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
DirectorySearcher search = new DirectorySearcher(ldapConnection);
search.Filter = string.Format("(&(ObjectClass=user)(userprincipalname={0}))", sid); // <-- this doesn't get custom properties
//search.Filter = string.Format("(employeeNumber={0})", "11663"); <-- this works
var result = search.FindOne(); // FindOne();
if (result.Properties.Contains("employeeNumber"))
{
//this never happens either :(
}
The above never returns the employeeNumber field, but if I uncomment the second search.Filter line and manually search by employeeNumber I find a result and it contains all of the fields that I need.
EDIT: I found a very good MSDN article Here which describes how to extend the UserPrincipal object to get custom attributes. The only issue is that it's giving me an empty string every time I access it even though I have verified that the property IS set in AD! Any help is appreciated.
EDIT 2: Here's the code for the custom principal extension:
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("Person")]
public class UserPrincipalExtension : UserPrincipal
{
public UserPrincipalExtension(PrincipalContext context)
: base(context)
{
}
public UserPrincipalExtension(PrincipalContext context, string samAccountName, string password, bool enabled)
: base(context, samAccountName, password, enabled)
{
}
public static new UserPrincipalExtension FindByIdentity(PrincipalContext context, IdentityType type, string identityValue)
{
return (UserPrincipalExtension)FindByIdentityWithType(context, typeof(UserPrincipalExtension), type, identityValue);
}
PersonSearchFilter searchFilter;
new public PersonSearchFilter AdvancedSearchFilter
{
get
{
if (searchFilter == null)
searchFilter = new PersonSearchFilter(this);
return searchFilter;
}
}
[DirectoryProperty("employeeNumber")]
public string EmployeeNumber
{
get
{
if (ExtensionGet("employeeNumber").Length != 1)
return string.Empty;
return (string)ExtensionGet("employeeNumber")[0];
}
set
{
ExtensionSet("employeeNumber", value);
}
}
}
And the custom search filter:
public class PersonSearchFilter : AdvancedFilters
{
public PersonSearchFilter(Principal p)
: base(p)
{
}
public void SAMAccountName(string value, MatchType type)
{
this.AdvancedFilterSet("sAMAccountName", value, typeof(string), type);
}
}
Usage:
UserPrincipalExtension filter = new UserPrincipalExtension(context);
filter.AdvancedSearchFilter.SAMAccountName(UserPrincipal.Current.SamAccountName, MatchType.Equals);
PrincipalSearcher search = new PrincipalSearcher(filter);
foreach (var result in search.FindAll())
{
var q = (UserPrincipalExtension)result;
var m = q.EmployeeNumber;
}
var m is always an empty string even though ALL AD entries have an employeeNumber.
EDIT: From active directory:
I am more confidant in how to fix your second method so I will answer that first. You need to specify the property in DirectorySearcher.PropertiesToLoad
when you do your search for the property to show up.
DirectorySearcher search = new DirectorySearcher(ldapConnection);
search.Filter = string.Format("(&(ObjectClass=user)(userprincipalname={0}))", sid);
search.PropertiesToLoad.Add("employeeNumber");
var result = search.FindOne(); // FindOne();
if (result.Properties.Contains("employeeNumber"))
{
//This should now work.
}
The reason it worked for string.Format("(employeeNumber={0})", "11663");
is because any search clause you add automatically gets put in to the PropertiesToLoad collection.
For your first method, I think you need to call DirectoryEntry.RefreshCache
and pass in the property to make it show up.
string sid = "";
using (PrincipalContext context = new PrincipalContext(ContextType.Domain))
{
UserPrincipal user = UserPrincipal.Current;
//sid = user.SamAccountName;
sid = user.UserPrincipalName;
//sid = user.Sid.ToString();
DirectoryEntry entry = user.GetUnderlyingObject() as DirectoryEntry;
entry.RefreshCache(new[] {"employeeNumber"});
if (entry.Properties.Contains("employeeNumber"))
{
}
}
But I am not 100% sure if that will work or not.
For your custom user principal, I am not sure what is wrong. The only difference I see between what you are doing and what I have in a project that does it that I know works is you use [DirectoryObjectClass("Person")]
but I have [DirectoryObjectClass("user")]
. Perhaps that is the issue
[DirectoryRdnPrefix("CN")]
[DirectoryObjectClass("user")] //Maybe this will fix it???
public class UserPrincipalExtension : UserPrincipal
{
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With