Get login user's Full Name when login to different domain windows authentication C# - active-directory

We have problem like, unable to get the user full name when reading from different domain.
eg: My userName is dom1\jsmith and full name is John Smith. When we deploy the project in dom1 domain, we are able to login and get the full name of the user. When we deploy the project in another domain(dom2) where the user(dom1\jsmith) has login permission, able to access the site but not able to get the full name.
We tried different solutions but didn't work.
//output: dom1\jsmith
User.Identity.Name;
//output: dom1\jsmith
string s = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
//output: dom1\jsmith
string sUserName = System.Environment.UserName;
//output: John Smith in same domain but not able to find identity
using (var context = new PrincipalContext(ContextType.Domain))
{
var principal = UserPrincipal.FindByIdentity(context, User.Identity.Name);
if (principal != null)
var fullName = string.Format("{0} {1}", principal.GivenName, principal.Surname);

Pass the name of the logon domain into the constructor for PrincipalContext. So split the DOMAIN\username that you have, and use just the domain portion:
var split = User.Identity.Name.Split('\\');
using (var context = new PrincipalContext(ContextType.Domain, split[0])) {
...
}
Although I am surprised it doesn't work as you have it, since it works for me and I log into a different domain than what my computer is joined to. Although in my case, the two domains are in the same AD forest. Maybe that's not the case for you.

Related

Blazor and Active Directory -Is getting it working here different than net core 5 MVC? And is that different that getting it working with Azure AD?

I have past net core 5 MVC applcation that I am rewriting in Blazor server.
The code I have used in MVC to get it working includes the following:
public void GetADinfo(out string givenName, out string surname, out string homePhone, out string email)
{
//===========================================================
//Go and get AD info for the current user or equivalent
var components = User.Identity.Name.Split('\\');
var username = components.Last();
// create LDAP connection object
DirectoryEntry myLdapConnection = createDirectoryEntry();
DirectorySearcher search = new DirectorySearcher(myLdapConnection);
search.Filter = "(cn=" + username + ")";
SearchResult result = search.FindOne();
DirectoryEntry dsresult = result.GetDirectoryEntry();
givenName = dsresult.Properties["givenName"][0].ToString();
surname = dsresult.Properties["sn"][0].ToString();
email = dsresult.Properties["mail"][0].ToString();
homePhone = dsresult.Properties["homePhone"][0].ToString();
//=============================================================================
}
public DirectoryEntry createDirectoryEntry()
{
// create and return new LDAP connection with desired settings
string ADconn = _context.ApplicConfs.Select(s => s.Ldapconn).FirstOrDefault();
string LDAPConn = _context.ApplicConfs.Select(s => s.Ldappath).FirstOrDefault();
//string ADconn;
//ADconn = "SERVER.A3HR.local";
//string LDAPConn;
//LDAPConn = "LDAP://SERVER.A3HR.local";
//DirectoryEntry ldapConnection = new DirectoryEntry("SERVER.A3HR.local");
//ldapConnection.Path = "LDAP://OU=staffusers,DC=leeds-art,DC=ac,DC=uk";
//ldapConnection.Path = "LDAP://SERVER.A3HR.local";
DirectoryEntry ldapConnection = new DirectoryEntry(ADconn);
ldapConnection.Path = LDAPConn;
ldapConnection.AuthenticationType = AuthenticationTypes.Secure;
return ldapConnection;
}
Does the code for using Active Directory in Blazor Server require anything different? In other words is the authentication different using Blazor as compared to net core MVC?
My app uses Blazor server windows authentication. So I get current user in the normal Blazor way. When I get that I want to use the current userid to look up the email and telephone number in AD and pre-populate it on a page if it exists. That way - in the application- the user doesn't have to re enter this information all the time.
Does anyone have an example of this using Blazor? Is the approach dramatically different between local AD and Azure AD in the logic/coding used? I see a few examples of Azure AD use in Blazor out there.
Thanks for any information provided...

Active Directory - Cross Domains

My role on the developer side. I have an application that I am trying check to see if a user has access to a share. In the application, I check the groups on that share. Then I check all the groups the user is in.
In one case, I not able to see the Local group that the users is in both code or the AD in windows
For example:
Domain A\User1 > Domain A\Global Group > Do not see: Domain B\Local Group
But when I look from Domain B I see:
Share > Domain B\Local Group > Domain A\Global Group > Do not see Domain A\User1
Is there some security setting that is not set correctly since I dont see in the windows tool or code.
Update
I have tried the following code. I am still unable to to see Domain B\Local Group.
string account = "{User**Or**Group}";
string domain = "{Domain}";
string dn = ADHelper.GetDistinguishedName(domain, account);
using (var forest = Forest.GetCurrentForest())
{
foreach (Domain domainName in forest.Domains)
{
Console.WriteLine(string.Format("Domain: {0}", domainName.Name));
Console.WriteLine("========================================================");
GetAllGroups(dn, domainName.Name);
domainName.Dispose();
}
}
void GetAllGroups(string dn, string domain)
{
DirectorySearcher ds = new DirectorySearcher(string.Format("GC://{0}", domain));
ds.Filter = String.Format("(&(distinguishedName={0}))", dn);
SearchResult sr = ds.FindOne();
if (sr == null)
return;
DirectoryEntry Diruser = sr.GetDirectoryEntry();
Diruser.RefreshCache(new string[] { "tokenGroups" });
for (int i = 0; i < Diruser.Properties["tokenGroups"].Count; i++)
{
SecurityIdentifier sid = new SecurityIdentifier((byte[])Diruser.Properties["tokenGroups"][i], 0);
try
{
NTAccount nt = (NTAccount)sid.Translate(typeof(NTAccount));
Console.WriteLine(nt.Value + "\t" + domain);
}
catch { }
}
}
In order to retrieve all groups user belongs to you have to query one Global Catalog in each domain of the entire forest for the user's membership (user's tokenGroups attribute will return you nested groups as well), then remove duplicated groups.
Be aware that Active Directory cannot return more than 5K values of a single attribute in one query.
If a user belongs to more than 10K groups, then AD will return you only first 5K. You have to use technique called range retrieval to query for membership in that case.
Also, there may be some external trusted domains that you also have to handle.
Other solution is to use GetEffectiveRightsFromAcl function to calculate effective user permissions for the specified share.
The solution is described here
Note that you will need to pass SE_OBJECT_TYPE.SE_LMSHARE as and object type to the function.

Azure Graph service not finding newly created user

I have a web application that uses Azure ACS and Azure AD to handle our authentication.
We have a user management feature in the web application that allows a user to create new users. This takes the details such as username, password, email etc. and uses the graph service to create a user in azure.
var newUser = new Microsoft.WindowsAzure.ActiveDirectory.User
{
userPrincipalName = user.UserName,
mailNickname = user.MailNickname,
accountEnabled = true,
displayName = user.FirstName + " " + user.Surname,
givenName = user.FirstName,
surname = user.Surname
};
newUser.passwordProfile = new PasswordProfile
{
forceChangePasswordNextLogin = false,
password = user.Password
};
var graphService = GetGraphService(tenantName);
graphService.AddTousers(newUser);
graphService.SaveChanges();
We are then required to create a record in the web application database for this user. The record needs the object ID from azure. So we use the graphService to get the newly-created user details. This is where my problem lies. It doesn't find the user.
private string GetObjectIdFromAzure(string userName, string tenantName)
{
var graphService = GetGraphService(tenantName);
var users = graphService.users;
QueryOperationResponse<Microsoft.WindowsAzure.ActiveDirectory.User> response;
response = users.Execute() as QueryOperationResponse<Microsoft.WindowsAzure.ActiveDirectory.User>;
var user = response.FirstOrDefault(x => x.userPrincipalName == userName);
return user != null ? user.objectId : "";
}
My code was working without any issues for a few months and only today I am having issues. What frustrates me more it that I have another deployment of the same code where it works without any issues. Some differences between the two deployments are:
The deployments use different Access control namespaces in Azure
The deployments have separate applications in Azure AD
One is https, one is http
The users for both system are under the same Directory.
I have put in logging in both deployments to get the number of users returned by
users.Execute()
In both systems it reported 100 (they share the same users)
Any ideas of what would cause this to stop working? I didn't change any code relating to this recently, I haven't changed any configuration on Azure and I didn't change the web.config of the application
The problem was caused by the fact that I was filtering the users after retrieving them. The graph API was only returning a maximum of 100 users.
So the process was like so:
User created in Azure
Success message returned
Web App searches Azure for user to get Object ID
Graph Api only returns top 100 users. User was not in top 100 alphabetically so error thrown
The reason it was working on our second deployment was that I was prefixing the user name with demo_ (we use this site to demo new features before releasing). This meant that it was being returned in the top 100 users.
I changed the code as follows so it filters during the retrieval instead of after:
private Microsoft.WindowsAzure.ActiveDirectory.User GetUserFromAzure(string userName, string tenantName, out DirectoryDataService graphService)
{
graphService = GetGraphService(tenantName);
var users = (DataServiceQuery<Microsoft.WindowsAzure.ActiveDirectory.User>)graphService.users.Where(x => x.userPrincipalName == userName);
QueryOperationResponse<Microsoft.WindowsAzure.ActiveDirectory.User> response;
response = users.Execute() as QueryOperationResponse<Microsoft.WindowsAzure.ActiveDirectory.User>;
var user = response.FirstOrDefault();
return user;
}

Two active directory forests, find corresponding exchange active directory object / mailbox

the company I work for has 2 Active Directory forests. One forest is called us where I log on in the morning with my profile (us\maflorin) and another forest is called (mail.us) which is dedicated to Exchange.
I have created an asp.net application that runs on SharePoint and gets the SPContext.Current.Web.CurrentUser.LoginName which is the us domain login name. (us\maflorin for example for me). I would like to get from the us credentials the corresponding object on the Exchange forest in order to write changes to the global address list (GAL) for user that opened the page after a line manager approval process.
I wrote the following working code to get the Exchange object, but it uses two ldap queries to find the object:
private Dictionary<string,AdRecod> FindExchangeAdProperties(string samAccountName,string description)
{
Dictionary<string,AdRecod> properties = null;
if (!string.IsNullOrEmpty(samAccountName))
{
properties = GetUserProperties(#"(&(objectCategory=person)(mailNickname=" +
samAccountName + "))");
if (properties != null) return properties;
}
if ((description == "") || (description == "0"))
throw new Exception("No matching Description, couldn't find correct Exchange AD object");
properties = GetUserProperties(#"(&(objectCategory=person)(description=" +
description + "))");
return properties;
}
Is it possible to get the exchange object with a single ldap query directly from the us samAccountName?
The mailNickname attribute on the exchange forest does not always match the sAMAccountName on the us forest. If it does not match, I use the second ldap query to see if a record is return by querying on the description field. The description field is many times the same for both forests but sometimes an administrator changed it.
Is it possible to find the corresponding Exchange Active Directory object for the us domain credentials more easily? How does Outlook find from the us credentials the corresponding mailbox / Ad object ? I was looking at the AD schema with adsiedit but could not find a clear field that is used to link the two forest objects together.
Furthermore I was looking into the Autodiscover service of the exchange web services managed api (mailbox dn attribute) but you need to pass into the GetUserSettings method an SMTP address and this field is not populated on the us domain.
Many thanks,
Mathias
I was able to find an answer to this question with a better approach than the one above which depends on the company's naming convention.
On the exchange forest I run a LDAP query with the DirectorySearcher class to obtain the attribute msExchMasterAccountSid.
The following code then provides the correct sam on the forest we use to logon:
var sid = directoryEntry.Properties["msExchMasterAccountSid"].Value as byte[];
// no mailbox
if (sid == null) continue;
var sidString = new SecurityIdentifier(sid, 0).ToString();
var samAccountName = "";
using (var context = new PrincipalContext(ContextType.Domain, "US"))
{
var principal = UserPrincipal.FindByIdentity(context, IdentityType.Sid, sidString);
if (principal == null) continue;
samAccountName = principal.SamAccountName;
}

Winform user authorization via active directory

I have a situation where I am using the following code to verify user membership in AD before executing tasks in my app
using System.Security.Principal;
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole("someGroup");
The above code works fine for machines on my domain, however I do have some machines which are not on my domain on which I have the WINFORM application installed. How can I verify the user membership in AD?
Edit - is there a way to prompt the windows login?
Since your computer is not joined to domain at all, we cannot use WindowsIdentity or WindowsPrincipal and then check its IsInRole() method. The IsInRole() method works only if your computer is joined to the domain and it's using your domain machine account to do S4USelf.
You cannot use LogonUser approach too because your computer won't let you create a logon session from an untrusted forest.
I think we can only query the Active Directory directly to get the information we want. The code in your posted Microsoft KB does not work very well as far as I can tell. It's trying to query from memberOf attribute. The group information is not always available from the memberOf attributes.
I just wrote an IsInRole() function using AccountManagement. I guess this is what you want. The IsInRole() function will call a recursive function IsInGroup() to find out all the groups the user belongs to.
private bool IsInRole(string domain, string username, string password, string role)
{
using (var context = new PrincipalContext(ContextType.Domain, domain, username, password))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(context, IdentityType.SamAccountName, role);
UserPrincipal user = UserPrincipal.FindByIdentity(context, IdentityType.SamAccountName, username);
return IsInGroup(user, group);
}
}
private bool IsInGroup(Principal principal, GroupPrincipal group )
{
if (principal.IsMemberOf(group))
return true;
foreach (var g in principal.GetGroups())
{
if (IsInGroup(g, group))
return true;
}
return false;
}
To use this IsInRole() function, you need to provide your domain name and domain credentials. If the username and password provided are wrong, you will get an exception.
You need .NET 3.5 SP1 to use AccountManagement API. Also, you may like to pay attention to this hotfix. The AccountManagement API got some bugs if running in some environment. You may need to apply the hotfix.

Resources