UserPrincipal doesn't let me add new user - c#

I'm having a bit of a problem when trying to add a new user or trying to access an already existing user in the Active Directory through my C# program.
var principalContext = new PrincipalContext(ContextType.Domain, "domain", "OU=Users,OU=SI");
UserPrincipal usr = UserPrincipal.FindByIdentity(principalContext, IdentityType.SamAccountName, samAccountStr);
It's throwing the "An operations error ocurred" exception. I found out that it's supposed to that if the user doesn't have the right permission for the Active Directory but I am running the program as administrator with the account of someone who can add users to the AD. So I really don't know what could be wrong. When I try to add a new user I try it like this:
var principalContext = new PrincipalContext(ContextType.Domain, "domain", "OU=Users,OU=SI");
UserPrincipal usr = new UserPrincipal(principalContext);
The code for the existing user already breaks when I call FindByIdentity. The code for the new user however breaks after I try to set some values for the new user principle. For example:
usr.Surname = sn;
The extended error says it's:
SvcErr: DSID-031007DF, problem 5012 (DIR_ERROR)
So any idea as to what might be causing it if it's not a permission problem?

You have to provide a full Distinguished Name for the OU, including the domain. This is not valid:
"OU=Users,OU=SI"
Something like this would be (if your domain was "domain.com"):
"OU=Users,OU=SI,DC=domain,DC=com"

Related

How to create AD User belonging to Domain Admins group in C#

I'm working on a C# project which auto-creates new Active Directory users and let them access to the AD server.
I have made a general user with the following code, but since the user does not belong to Domain Admins, it could not access to the server.
Domain domain = Domain.GetCurrentDomain();
PrincipalContext context = new PrincipalContext(ContextType.Domain);
UserPrincipal principal = new UserPrincipal(context);
principal.Name = name;
principal.UserPrincipalName = name + "#" + domain.Name;
principal.SamAccountName = name;
principal.Enabled = true;
principal.SetPassword(password);
principal.PasswordNeverExpires = true;
principal.Save();
Is there a way to include which group the new user belong in the code? Or after creating the account, adding the user to Domain Admins group might be another solution but I couldn't figure out how to do this either.
Any advice would be appreciated.
You just need to find the group and add the user you created. Like this:
var group = GroupPrincipal.FindByIdentity(context, "Domain Admins");
group.Members.Add(principal);
group.Save();
The code will have to run with credentials that can add someone to the Domain Admins group, which is likely a domain admin account itself.

handling multiple Domains for Active Directory Authentication

In my application I have the following method which I pass the AD Username to it (example: Domain1\User1) and try to find the groups that user belongs to in Active Directory.
public ActionResult Login(string userName)
{
PrincipalContext up = new PrincipalContext(ContextType.Domain);
UserPrincipal users = UserPrincipal.FindByIdentity(up,IdentityType.SamAccountName, userName);
PrincipalSearchResult<Principal> groups = users.GetGroups();
IEnumerable<string> userGroupList = groups.Select(p =>p.SamAccountName);
return userGroupList ;
}
The code works perfectly, However I am guessing this code will failto work in an environment where there are multiple domains.
For example:
1) User try to login with (Domain1\User1) and it will go through,
2) User try to login with (Domain2\User2) the app will try to look for User2 in Domain1, and becasue there is no such user in Domain1 it will fail.
Is this true? if yes, how can I resolve this issue so it will works with multiple domain?
I needed to pass the Domain name as the second parameter when trying to instantiate new PrincipalContext object.
By doing this I was able to retrieve user groups with multiple domains.
So the code will be something like this :
PrincipalContext up = new PrincipalContext(ContextType.Domain,"DomainName");

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.

Changing AD User Account Property by using the UserPrincipal

I am trying to change the User Account Property in Active Directory by using the UserPrincipal.
I have read that we have to use the special account which has the write access to the Active Directory rather than the current log on user. So, I created the special class to impersonate by using the Special Account. But I am still having the
System.UnauthorizedAccessException: General access denied error
at user.Save(ctx); line.
System.Security.Principal.WindowsImpersonationContext newUser = clsImpersonate.ImpersonateUser("ADUser", "ADPassword");
if (newUser != null)
{
PrincipalContext ctx = blAD.GetAdminPrincipalContext();
UserPrincipal user = blAD.GetUserPrincipal(this.SAMAccount);
user.Enabled = false;
user.Save(ctx);
newUser.Undo();
}
How can I achieve this requirement? Thanks.
What permissions have been delegated to your special user? It needs to be able to write userAccountControl on the users in question.
I wouldn't impersonate the account first off! Gain access through by passing the values through ad first.
For the real issue, look at the error:
Get the principalContect.
Get the userprincipal.
Do what you want to do.
Save it, why are u using undo? Delete the Undo().
To access the Principle as another user, define your PrincipalContext with the credentials of the user and use that PrincipalContext when getting the UserPrincipal.
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "domain.tld", "ADUser", "ADPassword");
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, IdentityType.SamAccountName, this.SAMAccount);
if (user != null)
{
user.Enabled = false;
user.Save();
}
If you are still getting the UnauthorizedAccess Exception, it is likely because the account you are specifying does not have access to write the userAccountControl attribute on the user object in Active Directory/LDS.

How to edit the registry keys of a specific user programatically?

I want to change a few settings of a Windows user that I created in my application. If I understand correctly, his "HKEY_CURRENT_USER" values will be under HKEY_USERS/<sid>/.... Is this correct? How can I get the sid of the user, if I know the user name and the domain?
Edit: How can I correctly edit the HKCU keys of that user, if I have the sid already?
I have a program that does exactly that. Here is the relevant part of the code:
NTAccount ntuser = new NTAccount(strUser);
SecurityIdentifier sID = (SecurityIdentifier) ntuser.Translate(typeof(SecurityIdentifier));
strSID = sID.ToString();
You will need to import two namespaces:
using System.DirectoryServices;
using System.Security.Principal;
Hope this helps.
Then use Registry.Users.SetValue with SID string\path to set the registry value.
This might not work as intended if you are editing a logged-off profile, especially a roaming profile.
There are two steps to this. First you must get the users sid. Second you must load the users registry hive. Other users hives are not loaded by default so you must load it explicitly.
The answer in Daniel White's comment is the best way to get the sid.
To load the user's registry hive, use the LoadUserProfile windows API via pinvoke. There is a complementary UnloadUserProfile to unload the hive when you are done with it.
You can use Query by example and search using PrincipalSearcher for appropriate UserPrincipal
// Since you know the domain and user
PrincipalContext context = new PrincipalContext(ContextType.Domain);
// Create the principal user object from the context
UserPrincipal usr = new UserPrincipal(context);
usr .GivenName = "Jim";
usr .Surname = "Daly";
// Create a PrincipalSearcher object.
PrincipalSearcher ps = new PrincipalSearcher(usr);
PrincipalSearchResult<Principal> results = ps.FindAll();
foreach (UserPrincipal user in results) {
if(user.DisplayName == userName) {
var usersSid = user.Sid.ToString();
}
}

Categories