.NET Forum / .NET Framework / New Users / July 2006
What permission do I need to add a user to a group? (C#)
|
|
Thread rating:  |
Brian Hampson - 23 Jul 2006 06:59 GMT I am trying to determine all the groups which the current user has permissions to add a member.
Here's my code:
foreach (System.DirectoryServices.SearchResult ADSearchres in ADSearch.FindAll()) {
//ActiveDs.ADSearchres.Properties["ntSecurityDescriptor"] ADChild = ADSearchres.GetDirectoryEntry(); foreach (ActiveDirectoryAccessRule ace in ADChild.ObjectSecurity.GetAccessRules(true, true, typeof(SecurityIdentifier))) { if (System.Security.Principal.WindowsIdentity.GetCurrent().Groups.Contains(ace.IdentityReference)) if (ace.AccessControlType == AccessControlType.Allow) { ActiveDirectoryRights rights = ace.ActiveDirectoryRights; if ((rights & ActiveDirectoryRights.WriteProperty) == ActiveDirectoryRights.WriteProperty) { GroupListAddItem(ADChild); break; }
} }
}
I had thougth that "ActiveDirectoryRights.WriteProperty" would have been the one I wanted. When I do this with a limited access user, though, it still lists all my groups :(
Suggestions? Help?
Brian Hampson System Administrator, North America ALS Laboratory Group, Environmental Division
Joe Kaplan (MVP - ADSI) - 23 Jul 2006 16:29 GMT Do you need to look this up for an arbitrary user, or the current user who is binding to the directory? If the former, there is a much easier way to accomplish this. AD (and ADAM) have a constructed attribute called "allowedAttributesEffective" which returns an array of strings listing all the attributes on a given object that can be modified by the security context that executed the search.
Essentially, you would just add "allowedAttributesEffective" to your PropertiesToLoad and then read the result of it for each object in your search results. If it contains the string "member", then you can change group memberships.
If you must do this via the security descriptor, it is both slower and more complex. A user might have rights to modify the membership via a variety of different types of ACEs such as a property write ACE, a control access right ACE for the "member" property set, a Full Control ACE, etc. It will be much harder to get this right. :) If possible, I recommend using the constructed attribute.
We have a few more details on this type of stuff in our book if you are interested (ch 7 and 8).
Joe K.
 Signature Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net --
>I am trying to determine all the groups which the current user has > permissions to add a member. [quoted text clipped - 40 lines] > System Administrator, North America > ALS Laboratory Group, Environmental Division Brian Hampson - 24 Jul 2006 03:10 GMT Bingo! Thanks Joe! It still takes a long time to enumerate all the groups for people that are Domain Admins, but it's quick and nice for our HelpDesk staff now :)
Cheers!
> Do you need to look this up for an arbitrary user, or the current user who > is binding to the directory? If the former, there is a much easier way to [quoted text clipped - 7 lines] > search results. If it contains the string "member", then you can change > group memberships. Joe Kaplan (MVP - ADSI) - 24 Jul 2006 04:05 GMT Glad that helped. It is a useful trick. :) There is also allowedChildClassesEffective if you want to know which objects a user can create as a child of another object. The only thing you can't figure out easily this way is which objects you can delete. :)
There isn't really any point of running this for a DA. DAs generally are given full control over all objects and can typically take ownership of any object that has that ACE removed, so there is no real way to prevent a DA from modifying just about anything. You should always assume that DAs can do anything they want to any object in the forest.
Joe K.
 Signature Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net --
> Bingo! Thanks Joe! It still takes a long time to enumerate all the > groups for people that are Domain Admins, but it's quick and nice for [quoted text clipped - 16 lines] >> search results. If it contains the string "member", then you can change >> group memberships. Brian Hampson - 24 Jul 2006 05:44 GMT Here's the gist of what I've created (some might call it a monster)
Our environment had a number of domain admins (which we are now working on pruning) People would create users and not put in alot of the information ("Too much work...") so I've created a front end app that will:
a) create the user b) create a home directory on a server of choice (based on which OU they are in) c) create a share for the home directory from that server. d) create an exchange mailbox on a designated mailserver (based on OU) and email address for the user. e) place the user in a default group based on which OU they are in. f) give the creator a list of other groups into which they have permission to add the user. (This was the part that I needed this last bit of help for)
This was all fine with DA's. Then we created a "HelpDesks" group. Suddenly things like D$ weren't available, remote WMI, Distributed COM, reading the GC for email addresses, all fell apart. The last few weeks I have been rewriting the code to handle the lower privs. When a "HelpDesk" user creates a new user, they are now only presented with the groups that they can "legally" add a user, not all :)
Now, like I said, my only problem is that my DA's get a list of about 500+ groups, and adding that process takes upwards of 60 seconds.
Here's what I'm doing for that:
ADNode = new System.DirectoryServices.DirectoryEntry("LDAP://" + domainstr); System.DirectoryServices.DirectorySearcher ADSearch = new System.DirectoryServices.DirectorySearcher(ADNode); ADSearch.CacheResults = true; ADSearch.PropertiesToLoad.Add("cn");
ADSearch.PropertiesToLoad.Add("allowedAttributesEffective"); ADSearch.Filter = "(&(objectCategory=group)(!cn=domain computers)(!cn=domain controllers))"; GroupList.Sorted = true; foreach (System.DirectoryServices.SearchResult ADSearchres in ADSearch.FindAll()) {
//ActiveDs.ADSearchres.Properties["ntSecurityDescriptor"] if (ADSearchres.Properties["allowedAttributesEffective"].Contains("member")) { ADChild = ADSearchres.GetDirectoryEntry(); GroupListAddItem(ADChild); if (ADChild.Name == "CN=G Env Env") GroupListAddSelectedItems(ADChild); } }
Note, the calls to "functions" are delegates for background thread update via invoke. Am I doing something horribly wrong?
> There isn't really any point of running this for a DA. DAs generally are > given full control over all objects and can typically take ownership of any > object that has that ACE removed, so there is no real way to prevent a DA > from modifying just about anything. You should always assume that DAs can > do anything they want to any object in the forest. Joe Kaplan (MVP - ADSI) - 24 Jul 2006 06:28 GMT I was really just trying to suggest that checking permissions for DA's probably isn't that helpful. If you could, you might consider determining if the user is a DA and not filtering your list if they are. You can do this by getting the user's tokenGroups attribute and seeing if the DA group SID is in there. You can also get this directly from the user's WindowsIdentity.Groups property (which is probably easier).
In our environment with >150K users and ~80K groups, this performance hit would really work. :) Neither would showing someone a list of 80K groups though. :)
It is probably best to avoid doing queries that return allowedAttributesEffective against many many objects, as that result is fairly expensive for the DC to produce. However, if you are trying to trim a list, it is hard not to use it.
A way to get much better perf would be to add your own attribute that contains the DNs of groups that are allowed to use that particular group and query against that. Then, of course, you need a way to keep the permissions delegations in sync with the contents of that attribute, but your UI will be much snappier and your DCs will be lest beaten up servicing that query.
It is weird that your helpdesk users can't search the GC. That should work fine. Authenticated Users can typically query the GC, just like the standard LDAP ports.
The rest of the stuff doesn't surprise me though. The way this kind of thing is often implemented is using a trusted subsystem design, where your client calls some server process that does have the credentials to perform the required operations, but then provides its own authorization layer to implement a role-based security mechanism. The role-based security mechanism is often a PITA to build and test, but it can gain you a bunch of important functionality. Some companies have whole products based on providing role-based delegation of operations against AD, so you could also consider buying something. :)
Best of luck with your solution. It sounds like you've made a lot of progress. We have a book out there that might have helped you with some of the stuff you are doing if you are interested.
Joe K.
 Signature Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net --
> Here's the gist of what I've created (some might call it a monster) > [quoted text clipped - 62 lines] >> can >> do anything they want to any object in the forest. Brian Hampson - 24 Jul 2006 07:07 GMT Thanks Joe. I really appreciate all the tips. I had originally planned on it being a "quick tool" and it has really grown. The reason I wrote it is because I wanted to learn some AD programming, and make my life easier. At least I got one of those two :)
The lines that caused the anonymous GC query were:
CDOEXM.IMailboxStore mailbox; mailbox = (IMailboxStore)userentry.NativeObject;
mailbox.CreateMailbox(MailServerList.SelectedValue.ToString()); userentry.CommitChanges();
//Give Mailbox some properties, like email address.
userentry.Properties["mailNickname"].Add(iFname.Text + "." + iLName.Text);
userentry.Properties["mail"].Add(ioemailaddress.Text + emaildomain.Text); userentry.CommitChanges();
The subsystem apparently makes the query as anon if you aren't an admin (or something like that) Anon read access to GC and things were happy.
> I was really just trying to suggest that checking permissions for DA's > probably isn't that helpful. If you could, you might consider determining [quoted text clipped - 109 lines] > >> can > >> do anything they want to any object in the forest. Joe Kaplan (MVP - ADSI) - 24 Jul 2006 16:21 GMT Ah, that is CDOEXM doing that. It is a terrible API and I hate it, but it is also the only supported method for mailbox-enabling objects in Exchange. Argh.
But yes, AD in 2003 doesn't allow anonymous queries (GC or LDAP port), so that would cause a problem. Yet another reason to hate CDOEXM! The crappy deployment model (install Exchange System Manager?!) and dependence on ADSI integration (can't be used directly from LDAP) and other weird behaviors with little value-add increase my loathing as well.
These "little" provisionings apps always do tend to get out of hand, don't they? :)
Joe K.
 Signature Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net --
> Thanks Joe. I really appreciate all the tips. I had originally > planned on it being a "quick tool" and it has really grown. The reason [quoted text clipped - 151 lines] >> >> can >> >> do anything they want to any object in the forest. Brian Hampson - 24 Jul 2006 16:41 GMT You took the words right out of my mouth. Perhaps .Net v4 or 5 will have nice native implementations for these things that don't make you jump through a few dozen hoops. 2.0 was a nice step up from 1.1 It's got to keep getting better!
Thanks for all your tips. I've never been a big book fan, but from what I've seen it looks like yours might make a nice addition to my thin collection :)
Cheers!
B.
> Ah, that is CDOEXM doing that. It is a terrible API and I hate it, but it > is also the only supported method for mailbox-enabling objects in Exchange. [quoted text clipped - 171 lines] > >> >> can > >> >> do anything they want to any object in the forest. Joe Kaplan (MVP - ADSI) - 24 Jul 2006 19:52 GMT Look for something they are calling the "Principal API" in the Orcas release timeframe. They actually showed it at Tech Ed 3 years ago, but then moved the release back.
The idea behind it is to provide wrappers around objects like Users and Groups, but with a focus on provisioning and lifecycle instead of authorization (like the current IPrincipal class). I think that may in fact be called .NET 4.0 or 5.0 by then. :)
Whether or not Exchange provisioning will still suck or not will depend a great deal on the Exchange team (and the version of Exchange you use), but I don't have high hopes...
Joe K.
 Signature Joe Kaplan-MS MVP Directory Services Programming Co-author of "The .NET Developer's Guide to Directory Services Programming" http://www.directoryprogramming.net --
> You took the words right out of my mouth. Perhaps .Net v4 or 5 will > have nice native implementations for these things that don't make you [quoted text clipped - 212 lines] >> >> >> can >> >> >> do anything they want to any object in the forest.
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|