Set Active Directory user properties using C# - c#

I am trying to set manager, department and title property of a user in active directory using C#.
Below is my code.
using (var context = new PrincipalContext(ContextType.Domain, "DomainName", "UserName", "Password"))
{
using (var userPrincipal = new UserPrincipal(context))
{
userPrincipal.SamAccountName = "UserSamACcountName";
using (PrincipalSearcher search = new PrincipalSearcher(userPrincipal))
{
UserPrincipal result = (UserPrincipal)search.FindOne();
DirectoryEntry directoryEntry = result.GetUnderlyingObject() as DirectoryEntry;
directoryEntry.Properties["manager"].Value = "<Manager Name>";
directoryEntry.Properties["title"].Value = "<Designation>";
directoryEntry.Properties["department"].Value = "<Department>";
directoryEntry.CommitChanges();
}
}
}
But I am getting below error when committing the changes.
A constraint violation occurred.
After debugging I found out that these properties (manager,title,department) are not available in DirectoryEntry properties collection. I could set "mailNickName" property without any error.
Does anyone has any solution?

Your code is technically correct, but you are sending an illigal value for the managerproperty.
The manager property is a distinguished name, not just the name of the person
directoryEntry.Properties["manager"].Value = "John Doe";
Will throw
A constraint violation occurred.
Change your code to something like:
directoryEntry.Properties["manager"].Value = "uid=john.doe,ou=People,dc=example,dc=com";

Related

InvalidProgramException throws on Microsoft.Office.Server.UserProfiles.UserProfileManager

I am making a webpart that needs to fetch the current user information from active directory using below code:
protected void fetchUserInfo()
{
System.Security.PermissionSet ps = new System.Security.PermissionSet(System.Security.Permissions.PermissionState.Unrestricted);
ps.Assert();
Microsoft.SharePoint.SPServiceContext serviceContext = Microsoft.SharePoint.SPServiceContext.Current;
UserProfileManager upm = new Microsoft.Office.Server.UserProfiles.UserProfileManager(serviceContext);
ProfileSubtypePropertyManager pspm = upm.DefaultProfileSubtypeProperties;
string userName = SPContext.Current.Web.CurrentUser.LoginName;
UserProfile profile = upm.GetUserProfile(userName);
foreach (ProfileSubtypeProperty prop in pspm.PropertiesWithSection)
{
}
}
However an InvalidProgramException throws on line ProfileSubtypePropertyManager pspm = upm.DefaultProfileSubtypeProperties;
The error message is:
Common Language Runtime detected an invalid program.
I tried googling
Common Language Runtime detected an invalid program. sharepointUserProfileManager but there aren't much info.
What could be the problem?
Just now saw that the exception flows on UserProfileManager, so the SPServiceContext isn't valid, and when I look at the property SiteSubscriptionId on serviceContext, I found it was 00000000-0000-0000-0000-000000000000
So what does it mean? is there any alternative way to get the current user info from sharepoint?
At last, I changed my mind using UserPrincipal to get the user info:
PrincipalContext _principalcontext = GetPrincipalContext();
UserPrincipal _user = UserPrincipal.FindByIdentity(_principalcontext, IdentityType.SamAccountName, UID);
if (_user != null)
{
DirectoryEntry directoryEntry = _user.GetUnderlyingObject() as DirectoryEntry;
//Property is here;
}

Exception while fetching data from Active Directory via C#

We are using the foll. code to retrieve the AD users and their details:-
We get error at line: SearchResultCollection resultCol =
search.FindAll();
Exception is: DirectoryServiceCOMException: An operations error
occurred. at System.DirectoryServices.DirectoryEntry.Bind(Boolean
throwIfFail) at System.DirectoryServices.DirectoryEntry.Bind()
at System.DirectoryServices.DirectoryEntry.get_AdsObject() at
System.DirectoryServices.DirectorySearcher.FindAll(Boolean
findMoreThanOne) at
SharePointProject20.VisualWebPart1.VisualWebPart1.GetADUsers()
public List<Users> GetADUsers()
{
try
{
List<Users> lstADUsers = new List<Users>();
string DomainPath = "LDAP://DC=SYSDOM,DC=local";
DirectoryEntry searchRoot = new DirectoryEntry(DomainPath);
DirectorySearcher search = new DirectorySearcher(searchRoot);
search.Filter = "(&(objectClass=user)(objectCategory=person))";
search.PropertiesToLoad.Add("samaccountname");
search.PropertiesToLoad.Add("mail");
search.PropertiesToLoad.Add("usergroup");
search.PropertiesToLoad.Add("displayname");//first name
SearchResult result;
SearchResultCollection resultCol = search.FindAll();
if (resultCol != null)
{
for (int counter = 0; counter < resultCol.Count; counter++)
{
string UserNameEmailString = string.Empty;
result = resultCol[counter];
if (result.Properties.Contains("samaccountname") &&
result.Properties.Contains("mail") &&
result.Properties.Contains("displayname"))
{
Users objSurveyUsers = new Users();
objSurveyUsers.Email = (String)result.Properties["mail"][0] +
"^" + (String)result.Properties["displayname"][0];
objSurveyUsers.UserName = (String)result.Properties["samaccountname"][0];
objSurveyUsers.DisplayName = (String)result.Properties["displayname"][0];
lstADUsers.Add(objSurveyUsers);
}
}
}
return lstADUsers;
}
catch (Exception ex)
{
return null;
}
}
public class Users
{
public string Email { get; set; }
public string UserName { get; set; }
public string DisplayName { get; set; }
public bool isMapped { get; set; }
}
What could be the issue?
Our domain name is SYSDOM.local
Could it be related to permissions (how do I verify this with network admin guys?), or do I need to explicitly pass username/password?
Code reference: http://www.codeproject.com/Tips/599697/Get-list-of-Active-Directory-users-in-Csharp
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. You can use a PrincipalSearcher and a "query-by-example" principal to do your searching:
// create your domain context
using (PrincipalContext ctx = new PrincipalContext(ContextType.Domain))
{
// define a "query-by-example" principal - here, we search for a UserPrincipal
UserPrincipal qbeUser = new UserPrincipal(ctx);
// create your principal searcher passing in the QBE principal
PrincipalSearcher srch = new PrincipalSearcher(qbeUser);
// find all matches
foreach(var found in srch.FindAll())
{
// do whatever here - "found" is of type "Principal" - it could be user, group, computer.....
}
}
If you haven't already - absolutely read the MSDN article Managing Directory Security Principals in the .NET Framework 3.5 which shows nicely how to make the best use of the new features in System.DirectoryServices.AccountManagement. Or see the MSDN documentation on the System.DirectoryServices.AccountManagement namespace.
Of course, depending on your need, you might want to specify other properties on that "query-by-example" user principal you create:
DisplayName (typically: first name + space + last name)
SAM Account Name - your Windows/AD account name
User Principal Name - your "username#yourcompany.com" style name
You can specify any of the properties on the UserPrincipal and use those as "query-by-example" for your PrincipalSearcher.
Constructing the PrincipalContext like shown in the sample will automatically connect to the current AD domain with the current user credentials. If you need to, you can specify other containers or domains to bind to, or you can also supply alternative credentials by using other overloads of the PrincipalContext constructor
The issue was resolved after using the HostingEnvironment.Impersonate() in PageLoad:-
Example:
using (HostingEnvironment.Impersonate()) {
GetADUsers();
}

Can i get a specific user by some attribute without knowing the OU's in Active Directory (AD)?

I need to get a specific user only knowing the "sAMAAccountName" field.
The thing is, this specific user can be inside of many groups:
OU=ThirdParty
OU=Company1
CN=User1
CN=User2
CN=User3
OU=Company2
CN=User1
CN=User2
CN=User3
Is there any way to get an user not knowing their groups, only using one attribute that they have?
My code:
DirectorySearcher search = new DirectorySearcher(_path);
search.Filter = "(&(objectCategory=person)(objectClass=User))";
StringBuilder groupNames = new StringBuilder();
try
{
SearchResultCollection result = search.FindAll();
.....
}
Thanks!
EDIT:
Ok, i got it using this code:
DirectorySearcher search = new DirectorySearcher(_entry, "(sAMAccountName=" + userCode + ")");
What information do you need to know about the user? We have used this type of code in the past to retrieve information about a user
using (var identity = new WindowsIdentity(username))
{
var user = new WindowsPrincipal(identity);
if (user.IsInRole("Some Role Name"))
return true;
return false;
}
EDIT
After your comment, I wonder if this post would provide you any further insite. They so show getting the field you're requesting, I'm just not sure the code to retrieve the employee will apply to you since this refers to InfoPath: http://msdn.microsoft.com/en-us/library/bb952744(v=office.12).aspx
If you switch to System.DirectoryServices.AccountManagement then you'll find out that the APIs are in fact much simpler.
For example:
public something FindUserByUserName( string UserName )
{
using ( var searcher =
new PrincipalSearcher( new UserPrincipal( ConfigurationContext ) { Name = UserName } ) )
{
var item = searcher.FindOne();
// do whatever you want with the found object and return it
}
}
where ConfigurationContext is a property which returns the PrincipalContext (credentials to connect to the AD, something like the "connection string")
Try this:
public static List<string> GetADUserInfo(string login)
{
//Using Hosting.HostingEnvironment.Impersonate()
List<string> info = new List<string>();
PrincipalContext infPC = new PrincipalContext(ContextType.Domain, "domain", "login", "password");
UserPrincipal infUP = new UserPrincipal(infPC);
PrincipalSearcher infPS = new PrincipalSearcher();
UserPrincipal foundUP;
infUP.SamAccountName = login;
infPS.QueryFilter = infUP;
foundUP = infPS.FindOne();
if (foundUP != null) {
info.Add(foundUP.SamAccountName.ToLower);
info.Add(foundUP.GivenName);
info.Add(foundUP.Surname);
info.Add(foundUP.EmailAddress.ToLower);
return info;
}
return null;
}

How do I set the ManagedBy property on a GroupPrincipal

I'm creating and updating Groups in Active Directory using the GroupPrincipal class in System.DirectoryServices.AccountManagement. When creating and updating, I also need to be able to set the ManagedBy property that you are able to set in the Managed By tab in the groups properties in the AD management console.
Can it be done programatically?
You cannot do this directly, unfortunately - but you can get access to the underlying DirectoryEntry and do it there:
PrincipalContext ctx = new PrincipalContext(ContextType.Domain, "YOURDOMAIN");
UserPrincipal toBeModified = UserPrincipal.FindByIdentity(".....");
UserPrincipal manager = UserPrincipal.FindByIdentity(ctx, "......");
DirectoryEntry de = toBeModified.GetUnderlyingObject() as DirectoryEntry;
if (de != null)
{
de.Properties["managedBy"].Value = manager.DistinguishedName;
toBeModified.Save();
}
You could extend the GroupPrincipal class and provide a ManagedBy property using the ExtensionSet method.
Take a look at this page. This is one of the best tutorials on AD in c#.
Some code that should work(untested) :
string connectionPrefix = "LDAP://" + ouPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
DirectoryEntry newGroup = dirEntry.Children.Add
("CN=" + groupName, "group");
group.Properties["sAmAccountName"].Value = groupName;
newGroup.Properties["managedBy"].Value = managerDistinguishedName;
newGroup.CommitChanges();
dirEntry.Close();
newGroup.Close();

How do I get the first name and last name of the logged in Windows user?

How I can get my first name last name with c# in my system (logging in windows with Active Directory username and pass)?
Is it possible to do that without going to the AD?
If you're using .Net 3.0 or higher, there's a lovely library that makes this practically write itself. System.DirectoryServices.AccountManagement has a UserPrincipal object that gets exactly what you are looking for and you don't have to mess with LDAP or drop to system calls to do it. Here's all it'd take:
Thread.GetDomain().SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
WindowsPrincipal principal = (WindowsPrincipal)Thread.CurrentPrincipal;
// or, if you're in Asp.Net with windows authentication you can use:
// WindowsPrincipal principal = (WindowsPrincipal)User;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain))
{
UserPrincipal up = UserPrincipal.FindByIdentity(pc, principal.Identity.Name);
return up.DisplayName;
// or return up.GivenName + " " + up.Surname;
}
Note: you don't actually need the principal if you already have the username, but if you're running under the users context, it's just as easy to pull it from there.
There is an easier way to do this:
using System.DirectoryServices.AccountManagement;
UserPrincipal userPrincipal = UserPrincipal.Current;
String name = userPrincipal.DisplayName;
This solution didn't work for me but this function worked great:
public static string GetUserFullName(string domain, string userName)
{
DirectoryEntry userEntry = new DirectoryEntry("WinNT://" + domain + "/" + userName + ",User");
return (string)userEntry.Properties["fullname"].Value;
}
You should call it that way:
GetUserFullName(Environment.UserDomainName, Environment.UserName);
(Found it here).
The problem with the approved answer is that if you have a policy of Lastname, Firstname in place, then DisplayName gives Smith, John, not John Smith. There are two ways to get the correct form, the userPrincipal.Name property contains "John Smith (jsmith1)" so you could use this, and just string.Split on "(". Or use the following:
private string ConvertUserNameToDisplayName(string currentSentencedByUsername)
{
string name = "";
using (var context = new PrincipalContext(ContextType.Domain))
{
var usr = UserPrincipal.FindByIdentity(context, currentSentencedByUsername);
if (usr != null)
name = usr.GivenName+" "+usr.Surname;
}
if (name == "")
throw new Exception("The UserId is not present in Active Directory");
return name;
}
This would give the required form "John Smith" as required by the original poster.
The fastest way is to bind directly to their AD object using their SID, which you already have. For example:
//ASP.NET
var identity = (WindowsIdentity) HttpContext.Current.User.Identity;
//Desktop
var identity = WindowsIdentity.GetCurrent();
var user = new DirectoryEntry($"LDAP://<SID={identity.User.Value}>");
//Ask for only the attributes you want to read.
//If you omit this, it will end up getting every attribute with a value,
//which is unnecessary.
user.RefreshCache(new [] { "givenName", "sn" });
var firstName = user.Properties["givenName"].Value;
var lastName = user.Properties["sn"].Value;
Had the same issue. After some research and browsing the Microsoft Docs, I found the solution.
First install the System.DirectoryServices.AccountManagement package using Nuget Package Manager.
Then while calling the GetUserNameWithoutDomain method pass the following as the parameters:
GetUserNameWithoutDomain(UserPrincipal.Current.GivenName, UserPrincipal.Current.Surname);
This should definitely work!

Categories

Resources