GetGroups vs GetAuthorizationGroups - c#

When I am using GetGroups instead of GetAuthorizationGroup, it results fine but the later one retrieve none even if group exists for the given user. The code I have used as below:
string userName = "userid";
PrincipalContext yourDomain = CurrentPrincipalContext;
UserPrincipal user = UserPrincipal.FindByIdentity(yourDomain, userName);
PrincipalSearchResult<Principal> groups = user.GetGroups();//user.GetAuthorizationGroups();
List<GroupPrincipal> result = groups.Select(x => (GroupPrincipal)x).ToList();
return result.Select(g => g.Name).ToList();
I can use GetGroups but the GetGroups method retrieve data in localhost but fetching none when pushed it into IIS 6.0 and thus I have to stick on GetAuthorizationGroups.
Please help me out....

GetAuthorizationGroups fails due to an obsolete SID in ActiveDirectory. Because GetAuthorizationGroups works recursively it could be a top-level SID or one attached to a top-level group. I'm including a link to hopefully help.
https://social.msdn.microsoft.com/Forums/vstudio/en-US/9dd81553-3539-4281-addd-3eb75e6e4d5d/getauthorizationgroups-fails-with-nomatchingprincipalexception?forum=csharpgeneral
NOTE: I've also been told that GetAuthorizationGroups caches the results somewhere for some length of time and isnt able to be cleared manually. Because of this I've had to write code to spelunk the Groups.

Related

Manipulate Windows "Users" group without access to domain?

I have a C#/WPF application that is used to manipulate local users and groups on a system. We only care about local users and groups, regardless of whether the machine is joined to a domain or not. When we create a user in our application, I want to add the user to the "Users" group. Normally this works fine, but if the machine is domain-joined and NOT connected to the network (e.g. a laptop out of the office), I get "the network path is not found" errors when trying to add a local user to the "Users" group.
I think the reason is because the "Users" group contains domain users, as shown in this screenshot.
And this is essentially my code:
public static void AddUserToGroup(UserPrincipal oUserPrincipal, string groupName)
{
using (PrincipalContext pc = new PrincipalContext(ContextType.Machine))
{
GroupPrincipal group = GroupPrincipal.FindByIdentity(pc, groupName);
if (group == null)
{
group = CreateLocalWindowsGroup(groupName);
}
if (!group.Members.Contains(oUserPrincipal)) // this line throws "network path not found" exception if the machine is domain joined, but can't contact the domain controller
group.Members.Add(oUserPrincipal);
group.Save();
}
}
I can't figure out how to approach this with the API, but it seems like it should be possible because I can add the exact same user to the same group manually with the "Local Users and Groups" tool with no issues, regardless of network connectivity. How can I get around this issue?
This is one reason I don't like using the AccountManagement namespace.
The GroupPrincipal.Members property returns a PrincipalCollection, which is just a collection of Principal objects. The actual type will be UserPrincipal or GroupPrincipal depending on what the actual member is.
But, those Principal classes, when they're created, load all the details for that object. So just the act of creating a UserPrincipal for a domain user triggers it to go out to the domain and get all the details for the user.
You're better off using DirectoryEntry directly, which is what the AccountManagement namespace uses in the background anyway. It gives you more control over what's actually happening.
var usersGroup = new DirectoryEntry($"WinNT://{Environment.MachineName}/{groupName}");
usersGroup.Invoke("Add", new object[] { $"WinNT://{Environment.MachineName}/{userName}" });
This assumes a userName variable with the name of the local user. If the user is already in the group, it will throw an exception, so you may want to catch that.
Besides actually working in this case, this will run faster since you're not wasting time collecting details for all the existing members when you have no intention of using any of that data.
Update: To read all the members of a local group, use .Invoke("Members"). Then you have to create a new DirectoryEntry with each member in the collection. For example:
foreach (var member in (IEnumerable) usersGroup.Invoke("Members")) {
using (var memberDe = new DirectoryEntry(member)) {
Console.WriteLine(memberDe.Name);
}
}
The DirectoryEntry class is really a wrapper around the Windows native ADSI Interfaces. For a group, the underlying object will really be IADsGroup. When you call .Invoke on a DirectoryEntry object, that lets you call the IADsGroup methods (you'll see the Members method listed in the documentation there). All of the object-specific classes like IADsGroup and IADsUser all inherit from IADs, so the methods from that are usable too.
This only applies to local groups. With Active Directory groups, you don't have to resort to using the IADs methods.

Why is my List.GetUserEffectivePermissions() method not working?

I'm developing a process in C# with the SharePoint 2013 Client Side Object Model. I need to retrieve the SharePoint List Permissions of a given user, that will be different than the user that is executing the code.
using SP = Microsoft.SharePoint.Client;
SP.ClientContext SpContext = new SP.ClientContext("SITEURL");
SP.Web SiteWeb = SpContext.Web;
SP.List Lst = SpContext.Web.Lists.GetByTitle("LIST");
var ClientUserEffPerms = Lst.GetUserEffectivePermissions(#"<domain>\<username>");
SpContext.Load(SiteWeb, S => S.EffectiveBasePermissions);
SpContext.Load(Lst, L => L.EffectiveBasePermissions);
SpContext.ExecuteQuery();
After this code executes, the ClientUserEffPerms.Value (BasePermissions) object does not represent the permissions of the given user correctly. The object isn't null, but it represents the user as having no permissions. The user has at minimum view and edit permissions and I can confirm this by viewing/editing List Items using the web browser as this user.
The code executing user has permission to enumerate permissions at both the Web and List level. I've confirmed this with the code below, both booleans resolve to true.
bool SvcUserHasSiteEnumPermsPerm = SiteWeb.EffectiveBasePermissions.Has(SP.PermissionKind.EnumeratePermissions);
bool SvcUserHasListEnumPermsPerm = Lst.EffectiveBasePermissions.Has(SP.PermissionKind.EnumeratePermissions);
Can anyone help me determine what is wrong with my GetUserEffectivePermissions() method?
When you call GetUserEffectivePermissions you need to pass in the full claims token version of the login name, which looks something like this:
i:0#.w|domain\user
You can get this by loading the LoginName property on a user object:
clientContext.Load(clientContext.Web.CurrentUser, i => i.LoginName);
clientContext.ExecuteQuery();
Of course, that's for the current user, so you'll need to acquire the user you actually want first.

Azure AD Graph API User memberOf nested groups

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/

Active Directory Group Membership Checking in .Net 4.5

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

Using UserPrincipal.FindByIdentity and PrincipalContext with nested OU

Here is what I am trying to achieve:
I have a nested OU structure that is about 5 levels deep.
OU=Portal,OU=Dev,OU=Apps,OU=Grps,OU=Admin,DC=test,DC=com
I am trying to find out if the user has permissions/exists at OU=Portal.
Here's a snippet of what I currently have:
PrincipalContext domain = new PrincipalContext(
ContextType.Domain,
"test.com",
"OU=Portal,OU=Dev,OU=Apps,OU=Grps,OU=Admin,DC=test,DC=com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, myusername);
PrincipalSearchResult<Principal> group = user.GetAuthorizationGroups();
For some unknown reason, the value user generated from the above code is always null. However, if I were to drop all the OU as follows:
PrincipalContext domain = new PrincipalContext(
ContextType.Domain,
"test.com",
"DC=test,DC=com");
UserPrincipal user = UserPrincipal.FindByIdentity(domain, myusername);
PrincipalSearchResult<Principal> group = user.GetAuthorizationGroups();
this would work just fine and return me the correct user. I am simply trying to reduce the number of results as opposed to getting everything from AD.
Is there anything that I am doing wrong? I've Googled for hours and tested various combinations without much luck.
Well, if
UserPrincipal.FindByIdentity(context, identityType, username) == null
then the user has not been found, which in your case probably is, because the user isn't defined in the OU= you are setting as container in your Context.
After much exploring, experimentation, googling and searching through stack overflow; it appears that .NET does not have a built in method to 'read' a particular OU that has a reference to an external Group that contains users as its members. Unfortunately, the suggested and recommended solution is to retrieve at domain level and perform some form of custom filtering.
Is the user you're looking for inside OU=Portal,OU=Dev,OU=Apps,OU=Grps,OU=Admin,DC=test,DC=com ?
What does your user object look like after your second search? What is it's DistinguishedName property?
The search you have in your first example will only search for objects inside that sub-sub-sub-sub-OU (the OU=Portal, .... that you have).
If your user exists in some other OU, then you have to search from the top of the domain - or inside the OU where the user actually exists (or any of its parents).
The user does not exist there, or you would not get null returned.
What is your end game? What do you mean by:
I am trying to find out if the user has permissions at
OU=Portal.
What type of permissions are you looking for? Admin delegation?
Hope this is of some help, I was having the same problem trying to retrieve groups from a nested OU. The structure of the ou was Groups > WebGroups. So I was writing the following...
var ctx = new PrincipalContext(ContextType.Domain, "domain", "OU=Groups,OU=WebGroups,DC=domain,DC=ie", "username", "password")
Turns out the order matters, WebGroups has to come first. When I changed it to the following my code worked...
var ctx = new PrincipalContext(ContextType.Domain, "domain", "OU=WebGroups,OU=Groups,DC=domain,DC=ie", "username", "password")
So I'm assuming you'd have to write "OU=Admin,OU=Groups... OU=Portal" to get yours working.

Categories