I am querying my Azure AD graph API for a user's group memberships.
I can make the query just fine, but the results are only the groups that the user DIRECTLY belongs to. None of the nested groups are listed.
I'm trying to find out if a user belongs to a specific group, but I don't want to have to make, what could end up to be, over 100 api calls to find out. (i.e. a user belongs to GroupA which is a member of GroupB which is a member of GroupC which is a member of GroupD. Groups B, C, and D do not show up in the user's list of groups even though he technically belongs to them - only GroupA).
Is there any way to get ALL group memberships in one API call or another? When I was using Integrated Windows Authentication, the IsInRole(GroupD) would have returned true. Though, that is not available with Azure AD authentication and any of the postings that claim to have code to re-implement IsInRole are simply making the same call to user.memberOf (which does not do the nested groups).
Just for reference, this is the code where I make the call.
var client = AuthenticationHelper.GetActiveDirectoryClient();
var user = await client.Users.GetByObjectId(objectId).ExecuteAsync();
var userFetcher = (IUserFetcher)user;
var pagedCollection = await userFetcher.MemberOf.ExecuteAsync();
do
{
var directoryObjects = pagedCollection.CurrentPage.ToList();
foreach (var group in directoryObjects.OfType<Group>().Select(directoryObject => directoryObject))
{
groupMembership.Add(group);
}
pagedCollection = await pagedCollection.GetNextPageAsync();
}
while (pagedCollection != null && pagedCollection.MorePagesAvailable);
Yes. The getMemberObjects API returns all groups (transitive) of which the user is a member: https://msdn.microsoft.com/en-us/library/azure/dn835117.aspx . Also, using the checkMemberGroups API you can check whether or not the user is member of a group (transitively): https://msdn.microsoft.com/en-us/library/azure/dn835107.aspx
However for your requirement the application roles feature of Azure AD might be a better fit. Your application can declare application roles, that can be assigned by the admin to users as well as groups. When the users signs in to your application, Azure AD computes the application roles that have been assigned to them and sends them in the roles claim. No need to query graph at runtime. See here: http://www.dushyantgill.com/blog/2014/12/10/roles-based-access-control-in-cloud-applications-using-azure-ad/
Related
I'm trying to get the list of AD groups a user belongs to, but for some strange reason ClaimsIdentity brings back all the user's information except the groups they belong to.
When I try to implement solutions from previous .NET versions it tells me that these libraries are deprecated.
Code Example:
var claims = ClaimsIdentity.Claims.Where(q => q.Type == ClaimTypes.GroupSid).Select(q => q.Value);
foreach (var claim in claims)
{
var name = new SecurityIdentifier(claim).Translate(typeof(NTAccount)).ToString();
}
This returns me the clients entity but when I explore the result I don't receive the group tag, what am I doing wrong?
I'm using azure active directory to control user access to my web app. This all works well, but I cant figure out how to identify which group the currently logged in user is a member of. In ClaimsIdentity I can see both groups setup in azure, but I cant determine which one of these groups the user is a member of (they will only belong to 1 of 2 groups). I have this code and also a key in my web.config that matches the key of my admin user, but both of my groups are in the claimsidentity object. So how can I determine if this user is in my admin group ?
var groups = identity.Claims.Where(x => x.Type.Equals("groups")).ToList();
//this is a GUID that should match the group objectID for Adminusers in the azure active directory
string admin = Helpers.Settings.AdminUser;
if (groups.Any(c => c.Value.Contains(admin)))
{
return true;
}
else
{
return false;
}
I must be going about this the wrong way, anyone help me out ?
It seems you have enabled Group Claims to check a user’s membership in a specific security group (or groups).
The group claims will return a collection of the Groups and DirectoryRoles that current user is a member of . For example , if user is a global administrator in your AAD , and belongs to one group . With group claims you will get two records(1 groups and 1 directory role) .
If you to want to get all of the groups(no DirectoryRoles) that the user has direct or transitive membership in , we could call the getMemberGroups function using Azure AD Graph API .
In your scenario , to check whether user is in your admin group , you can check whether object ID of admin group exists in groups claim . If exists ,the user belongs to admin group .
I have a pretty simple task – given the list (from file) of user names and their emails, synchronize it with the members of AD (Active Directory) users, meaning – add users that exist in the list and missing in AD group, and remove users that exist in AD group and missing in the list. The complication is that the company is very big and uses a lot of domains around the world based on their location – let’s say it.comany.com, fr.company.com, uk.company.com, us.company.com, etc. – each of them contains tons of users and users to add can be in any of them.
I use the following code to find the AD group:
var groupSearch = new DirectorySearcher(new DirectoryEntry(“LDAP://company.com”), “(&(ObjectClass=Group)(CN=TheNameOfTheADGroup))”);
var dirEntry = new DirectoryEntry(groupSearch.FindOne().Path);
and it finds the group successfully.
Now, I need to add user to this group (knowing only his username, like willsmith). This can be done by calling
dirEntry.Invoke(“Add”, new object[] { dirPath })
where dirPath is the FULL LDAP path of the user. I can get it by searching in specific domain, e.g.:
var userSearch = new DirectorySearcher(new DirectoryEntry(“LDAP://xx.company.com”), “(&(ObjectClass=User)(CN=willsmith))”);
var dirPath = userSearch.FindOne().Path;
//and then use dirPath in Invoke(“Add”…) – see above
but the problem is that I don’t know which exact domain the user is in, and the number of domains is around 30, each user search takes about 2-3 seconds, so it will be a very long process.
Any ideas how to define domain for the user? Or maybe some other solution for the problem?
P.S. I do not use the new AD management capabilities introduced in .NET 3.5 (PrincipalContext, GroupPrincipal.FindByIdentity), because they don’t allow to specify the root for the search (which was the first parameter in DirectorySearcher constructor), which I need because the AD group is located in company.com domain not us.company.com from where is my account.
I have an ASP.Net MVC application using Windows Authentication, and I am checking group membership for security on controller actions.
Simple as it sounds, I've found no other Question that can resolve the problem I am experiencing.
First Attempt: [Authorize]
The classic method is to simply slap an Authorize data annotation attribute on the controller action and go to town:
[Authorize(Roles = #"domain\groupName1")]
No dice. I am prompted for credentials. Usually this means something is wrong with the Windows Authentication configuration but it's setup fine: (1) HttpContext.User is a WindowsPrincipal object, and (2) I confirmed another known group name works.
Second Attempt: IsInRole()
The next step taken was to go a more old fashioned route and use IPrincipal.IsInRole(), and again, one returns false, the other true.
var wp = (WindowsPrincipal)User;
// false
var inGroup1 = wp.IsInRole(#"domain\groupName1");
// true
var inGroup2 = wp.IsInRole(#"domain\groupName2");
Stumped... so I hit up my systems nerds and we double check everything. User is a group member? Yes. Group name is spelled correctly? Yes. The next step was to snag the SID.
Third Attempt: Search Identity's Group Collection
In my controller I check the WindowsIdentity and look through the group collection for the SID of the troublesome group:
var wi = (WindowsIdentity)wp.Identity;
var group = wi.Groups.SingleOrDefault(g => g.Value == "group1-sidValue");
The group variable is the SecurityIdentifier object. Because it is not null, we can be certain that this current user is a member of the group that both the [Authorize()] or IsInRole() attempts fail to confirm.
Fourth Attempt: DirectoryServices.AccountManagement
At this point, I'm going nuts and add reference to the AccountManagement APIs. I search the domain context for the GroupPrincipal by both name and SID:
var pc = new PrincipalContext(ContextType.Domain, "domain");
var gp1byName = GroupPrincipal.FindByIdentity(pc, "groupName1")
var gp1bySid = GroupPrincipal.FindByIdentity(pc, IdentityType.Sid, "group1-sidValue");
Both group principal variables are ripe with the same object, and I verified through a watch variable that the principal's Members collection contains a UserPrincipal object with the same SID as the current WindowsPrincipal on HttpContext.
Question:
What in the hell have I missed here? Why would both role checking methodologies fail when it is plain and clear through object exploration that the user is a valid member of this given group?
The fact that one group checks fine and the other does not seems the most strange part at this point.
Answer:
Essentially it's translation issues between WindowsIdentity and NTAccount (both of these System.Security.Principal) and lastly, the actual Active Directory entry.
When validating a WindowsIdentity against AD, if you want to use anything other than the Sam or the Sid, you will need to use System.DirectoryServices.AccountManagement.
Caveat: In .Net 4.5 the security principals include Claims but that's out of context.
Long Explanation:
In a Windows Authenticated web application, HttpContext.User is a WindowsPrincipal object wrapping an underlying WindowsIdentity.
WindowsIdentity has for most intents and purposes only two properties with which the authenticated user can be identified: Name and User.
These properties translate to two properties on the identity's corresponding AD account entry:
WindowsIdentity.Name = SamAccountName
WindowsIdentity.User = SID
The [Authorize] filter attribute ultimately calls IsInRole(string role) on the underlying principal... and the IsInRole() string overload instantiates an NTAccount with the role (the "SamAccountName" in an AD entry).
This explains the failure in #1 and #2 above.
To authorize the HttpContext.User against anything but his/her Sid or SamAccountName, you'll need DirectoryServices.AccountManagement or classic LDAP.
I have an ASP.Net MVC application using Windows Authentication, and I am checking group membership for security on controller actions. Simple as it sounds, I've found no other Question that can resolve the problem I am experiencing.
Took me a lot of time to find something
http://www.c-sharpcorner.com/uploadfile/scottlysle/test-for-user-group-membership-in-Asp-Net-C-Sharp/
My code to check if user belongs to an AD group :
foreach (System.Security.Principal.IdentityReference group in System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
{
if (String.Equals(group.Translate(typeof(System.Security.Principal.NTAccount)).ToString(), #"your_domain_name\your_group_name", StringComparison.InvariantCultureIgnoreCase))
{
// the user belongs to a group
}
}
That is all I needed beside <authentication mode="Windows"/> in Web.config file
In my Sharepoint code I display a list of all defined users via:
foreach (SPUser user in SPContext.Current.Web.AllUsers)
{
...
}
The great part is, I can add a domain security group to a Sharepoint group (like Visitors) thus adding many users at once (simpler administration). But my code doesn't see those users at least not until they log-in for the first time (if they have sufficient rights). In this case I can only see the domain security group SPUser object instance with its IsDomainGroup set to true.
Is it possible to get domain group members by means of Sharepoint without resorting to Active Directory querying (which is something I would rather avoid because you probably need sufficient rights to do such operations = more administration: Sharepoint rights + AD rights).
You can use the method SPUtility.GetPrincipalsInGroup (MSDN).
All parameters are self-explaining except string input, which is the NT account name of the security group:
bool reachedMaxCount;
SPWeb web = SPContext.Current.Web;
int limit = 100;
string group = "Domain\\SecurityGroup";
SPPrincipalInfo[] users = SPUtility.GetPrincipalsInGroup(web, group, limit, out reachedMaxCount);
Please note that this method does not resolve nested security groups. Further the executing user is required to have browse user info permission (SPBasePermissions.BrowseUserInfo) on the current web.
Update:
private void ResolveGroup(SPWeb w, string name, List<string> users)
{
foreach (SPPrincipalInfo i in SPUtility.GetPrincipalsInGroup(w, name, 100, out b))
{
if (i.PrincipalType == SPPrincipalType.SecurityGroup)
{
ResolveGroup(w, i.LoginName, users);
}
else
{
users.Add(i.LoginName);
}
}
}
List<string> users = new List<string>();
foreach (SPUser user in SPContext.Current.Web.AllUsers)
{
if (user.IsDomainGroup)
{
ResolveGroup(SPContext.Current.Web, user.LoginName, users);
}
else
{
users.Add(user.LoginName);
}
}
Edit:
[...] resorting to Active Directory querying (which is something I would rather avoid because you probably need sufficient rights to do such operations [...]
That's true, of course, but SharePoint has to lookup the AD as well. That's why a application pool service account is required to have read access to the AD.
In other words, you should be safe executing queries against the AD if you run your code reverted to the process account.
I would suggest you just query Active Directory directly. You are spending a lot of effort to try to get SharePoint to make this call to AD for you. Every account that has Domain User access should be able to query the AD groups you have nested in SharePoint. I would just go to the source.
This way you don't have to worry about Browse User Permissions or anything else. In my opinion trying to proxy this through SharePoint is just making your life more difficult.