Getting users from another AD Domain using PrincipalContext - c#

How would I get users from an AD Group which contains users from different domains.
For example, I have 2 domains in my active directory, Domain1.corp.com and Domain2.corp.com
I have an AD group called TestGroup which contains users from both the domains.
Domain1 users: TestUser1, TestUser2
Domain2 users: TestUser3, TestUser4, TestUser5
TestGroup users: TestUser1, TestUser2, TestUser3, TestUser5
Following could would return only Domain1 users.
string domainname = "Domain1.corp.com:3268";
string usernames = String.Empty;
using (var p_context = new PrincipalContext(ContextType.Domain, domainname))
{
using (var group = GroupPrincipal.FindByIdentity(context, "TestGroup"))
{
var users = group.GetMembers(false);
foreach (var user in users)
{
username = username + ", " + user.SamAccountName;
}
}
}
When returning the username variable, I would see users from only Domain1.Am I missing anything over here?
My IIS server is located in the Domain1.corp.com
I verified that the server had access to the other domain by running a powershell script which returned users located in both the domains.
get-adgroupmember "TestGroup" -recursive
Ref: https://stackoverflow.com/a/7073023/326315

You need to use the underlying System.DirectoryServices.DirectoryEntry for the group:
var groupEntry = (DirectoryEntry)group.GetUnderlyingObject();
(Note: according to MSDN GetUnderlyingObject() will return a DirectoryEntry, even though the return type is object.)
From there you can get the member distinguished names:
var memberDistinguishedNames = groupEntry.Properties["member"].Cast<string>();
The users from the other domain are Foreign Security Principals so the distinguished name will be in the form CN={SecurityIdentifier},CN=ForeignSecurityPrincipals. You can extract the Security Identifier and search for the user on the other domain/s in order to get the rest of their details. Unfortunately this does mean you need to connect to both domains, which can be slow.

Related

Retrieving all Users using Google Apps Provisioning API?

I am trying to retrieve all users in domain but it's never work. I never got any error in my code.
I am adding as reference Google.GData.Apps.dll in my project. I am using Google Apps Provisioning API. I am referring these link https://developers.google.com/google-apps/provisioning/
Code :-
string domain = #"<domain name>";
string subs = #"<Authorization code>";
AppsService appService = new AppsService(domain, subs);
// appService.CreateUser("john.jain","James","anderson","james#1234");
// appService.DeleteUser("Amitesh");
appService.RetrieveAllUsers();
The following works for me - RetrieveAllUsers() returns a UserFeed object containing all users. Note that I am using a different constructor for the apps service (using username/password credentials and not OAuth).
this.appsService = new AppsService(credential.Domain, credential.Username, credential.Password);
UserFeed userFeed = this.appsService.RetrieveAllUsers();
// Selecting all users except domain super administrators.
var usernamesInDomain =
(from UserEntry user in userFeed.Entries
where user.Login.Admin != true
select user.Login.UserName).ToList();
What does the returned UserFeed object contain in your case?
This is my solution :-
Google.GData.Apps.UserFeed s = appService.RetrieveAllUsers();
foreach (UserEntry s1 in s.Entries)
{
string username = s1.Login.UserName.ToString();
}

UserPrincipal.FindByIdentity error in Firefox

I have a method to retrieve a list of AD groups that a user belongs to. Here is the code:
public static List<GroupPrincipal> GetGroups(string userName)
{
List<GroupPrincipal> result = new List<GroupPrincipal>();
// establish domain context
PrincipalContext yourDomain = new PrincipalContext(ContextType.Domain);
UserPrincipal user = null;
// find your user
user = UserPrincipal.FindByIdentity(yourDomain, userName);
// if found - grab its groups
if (user != null)
{
PrincipalSearchResult<Principal> groups = user.GetGroups();
// iterate over all groups
foreach (Principal p in groups)
{
// make sure to add only group principals
if (p is GroupPrincipal)
{
result.Add((GroupPrincipal)p);
}
}
}
return result;
}
In both IE and Chrome, this can work fine, but in Firefox, it always gives me DirectoryServicesCOMException on the user = UserPrincipal.FindByIdentity(yourDomain, userName); I don't even have any idea what kind of exception that is. Can someone explain me what the error is and how to fix it? Thank you so much!
Change the call to look like this:
using (HostingEnvironment.Impersonate()){
user = UserPrincipal.FindByIdentity(yourDomain, userName);
}
You will need to make sure that your application pool has AD permissions. This will perform the underlying AD call using the credentials of the hosting environment (the web App Pool Identity) instead of the credentials of user, who may not have permissions to query the AD server.

Ldap connection in .net C#

I have an application where I can send emails. Now am asked to use ldap to authenticate the user email. Am very new to this concept. I have been given a ldap server link. No idea how to proceed with that. Any article or hits will be greatly helpful.
Here is the code am trying with
public static UserDetail GetUserDetails(string EmailId, string domainName)
{
UserDetail userDetail = new UserDetail();
try
{
string filter = string.Format("(&(ObjectClass={0})(sAMAccountName={1}))", "person", EmailId);
string[] properties = new string[] { "fullname" };
DirectoryEntry adRoot = new DirectoryEntry("LDAP://" + domainName, null, null, AuthenticationTypes.Secure);
DirectorySearcher searcher = new DirectorySearcher(adRoot);
searcher.SearchScope = SearchScope.Subtree;
searcher.ReferralChasing = ReferralChasingOption.All;
searcher.PropertiesToLoad.AddRange(properties);
searcher.Filter = filter;
SearchResult result = searcher.FindOne();
DirectoryEntry directoryEntry = result.GetDirectoryEntry();
string displayName = directoryEntry.Properties["displayName"[0].ToStrin();
string firstName = directoryEntry.Properties["givenName"][0].ToString();
string lastName = directoryEntry.Properties["sn"][0].ToString();
string emailId = directoryEntry.Properties["mail"][0].ToString();
userDetail.EmailId = emailId;
}
catch (Exception)
{
}
return userDetail;
}
I want to achieve it on click of search button. How do I call the method and pass variables.
If you're on .NET 3.5 or newer, you can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// define a "query-by-example" principal - here, we search for a UserPrincipal
// and with the e-mail of "bruce#example.com"
UserPrincipal qbeUser = new UserPrincipal(ctx);
qbeUser.EmailAddress = "bruce#example.com";
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// try to find that user
UserPrincipal found = srch.FindOne() as UserPrincipal;
if(found != null)
{
// do whatever here - "found" is the user that matched the e-mail given
}
else
{
// there wasn't any user with that e-mail address in your AD
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
Of course, depending on your need, you might want to specify other properties on that "query-by-example" user principal you create:
DisplayName (typically: first name + space + last name)
SAM Account Name - your Windows/AD account name
User Principal Name - your "username#yourcompany.com" style name
You can specify any of the properties on the UserPrincipal and use those as "query-by-example" for your PrincipalSearcher.
Given the input of emailAddress (type string) this code will search the LDAP directory for a user with a matching email address and return some information on the user:
string fullName = string.Empty;
string givenName = string.Empty;
string distinguishedName = string.Empty;
string sAMAccountName = string.Empty;
using (var context = new PrincipalContext(ContextType.Domain, "DOMAIN"))
{
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (Principal result in searcher.FindAll())
{
var de = result.GetUnderlyingObject() as DirectoryEntry;
if (de.Properties["cn"].Value.ToString().Contains(" "))
{
//var userEntry = new DirectoryUser(de.Properties["sAMAccountName"].Value.ToString());
var currentUserEmail = de.Properties["mail"].Value.ToString().ToLower();
if (currentUserEmail == emailAddress)
{
if (de.Properties["cn"].Value != null)
fullName = de.Properties["cn"].Value.ToString();
if (de.Properties["givenName"].Value != null)
givenName = de.Properties["givenName"].Value.ToString();
if (de.Properties["distinguishedName"].Value != null)
distinguishedName =de.Properties["distinguishedName"].Value.ToString();
if (de.Properties["sAMAccountName"].Value != null)
sAMAccountName = de.Properties["sAMAccountName"].Value.ToString();
}
}
}
}
}
It requires a reference to :
System.DirectoryServices;
System.DirectoryServices.AccountManagement;
One caveat I would like to mention is, directory look up routines can be quite slow. If you have 100,000 users on your domain, this process will take a while to run. WHat I tend to do, is dump the output of a directory search to a database table on a regular basis, and perform any lookups on that table. The frequency of the database dumps will of course depend on your business logic. Sometimes I simply truncate the table before performing a new dump, and in other circumstances, I dump to a 'staging' table, and only apply 'delta' updates to the active directoy record table.
Connect to the directory server, using SSL if possible. Promoting a non-secure connection to a secure connection with the StartTLS extended operation is also possible.
Transmit a SEARCH request to the server that contains the base DN from which the client wishes the search to begin, the scope of the search (base, one-level, or sub-tree), a filter using the information the LDAP client knows that will narrow the search results to the desired user, and the attribute 1.1.
The server will respond with the a SEARCH response containing the number of entries that matched the search request parameters and the distinguished names of each entry that matched.
Transmit a BIND request to the directory server over the secure connection. The BIND request contains a distinguished name and the credentials for the distinguished name
The directory server will verify the credentials and return a BIND response with an integer result code indicating whether the credentials matched those stored in the server database
see also
LDAP: programming practices
LDAP: search practices

Active Directory PrincipalContext.ValidateCredentials domain disambiguation

I'm dealing with two domains - one is a trusted domain. There may be a JohnSmith on one domain and another JohnSmith on the other. Both of these people need to log into my application.
My problem: it doesn't matter which domain I pass in - this code returns true! How do I know which JohnSmith is logging in?
static public bool CheckCredentials(
string userName, string password, string domain)
{
using (var context = new PrincipalContext(ContextType.Domain, domain))
{
return context.ValidateCredentials(userName, password);
}
}
The ValidateCredentials works with userPrincipalName you perhaps can try to build the first parameter (username) combining the login and the domain to create the username JohnSmith#dom1.com versus JohnSmith#dom2.com.
You can always retrieve the full DN of the user who has logged in using
UserPrincipal up = UserPrincipal.FindByIdentity(pc, IdentityType.SamAccountName, userName);
up.UserPrincipalName // shows user#domain.com
up.DistinguishedName // shows CN=Surname,OU=group,DC=domain,DC=com
up.SamAccountName // shows login name
Use the up.SamAccountName to subsequent calls to ValidateCredentials including the domain name - you can't have 2 users who log in using the same sAMAccountName after all!
The DistinguishedName will definitely show you which JohnSmith logged in.
Based on JPBlanc's answer, I've re-written my code. I've also added a try/catch in case a bogus domain is passed in.
static public bool CheckCredentials(
string userName, string password, string domain)
{
string userPrincipalName = userName + "#" + domain + ".com";
try
{
using (var context = new PrincipalContext(ContextType.Domain, domain))
{
return context.ValidateCredentials(userPrincipalName, password);
}
}
catch // a bogus domain causes an LDAP error
{
return false;
}
}
The accepted answer will fail with Domains that contain different email addresses within them. Example:
Domain = Company
User1 = employee#department1.com (under company Domain)
User2 = employee2#Department2.com (under company Domain)
The provided answer will return false using:
userName = "employee";
domain = "company";
string userPrincipalName = userName + "#" + domain + ".com";
The correct way to encompass users across domains is:
string userPrincipalName = userName + "#" + domain;
without the .com portion it searches the user AT that domain instead of searching for an email within a global domain.

Get UPN or email for logged in user in a .NET web application

I'm not a .NET developer, and I have a feeling this would be trivial for someone who is:
I have a C# web application that makes user of the user credentials of the logged in user. Currently it uses the SID which comes from
System.Security.Principal.WindowsIdentity.GetCurrent().User.Value
I need to get either the users UPN login or email address (as defined in active directory) instead of the SID. GetCurrent() returns an object of type WindowsIdentity; looking in the details for WindowsIdentity Members:
MSDN: WindowsIdentity Members
I can't see anything that looks like it would give me either the UPN or email in there. How can I pull up that information to use, either by feeding the SID into some other function or calling something different in the first place.
Meanwhile (.NET 3.5) this is a one-liner:
System.DirectoryServices.AccountManagement.UserPrincipal.Current.EmailAddress
for the email, or
System.DirectoryServices.AccountManagement.UserPrincipal.Current.UserPrincipalName
for the UPN.
To query active directory using a directory searcher you need to do something like this (totally untested code):
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
string ldapPath = "LDAP://domain.company.com";
public string GetEmail(string userName, string ldapPath)
{
using (DirectoryEntry root = new DirectoryEntry(ldapPath))
{
DirectorySearcher searcher = new DirectorySearcher(root);
searcher.Filter = string.Format(#"(&(sAMAccountName={0}))", userName);
searcher.PropertiesToLoad = "mail";
SearchResult result = searcher.FindOne();
if (result != null)
{
PropertyValueCollection property = result.Properties["mail"];
return (string)property.Value;
}
else
{
// something bad happened
}
}
}
Try:
System.Security.Principal.WindowsIdentity.GetCurrent().Name

Categories

Resources