I have written the following program which connects to two LDAP stores, compares the attributes and based on the outcome, creates a new csv file. I have run into a problem though.
Here is the code:
//Define LDAP Connection
string username = "****";
string password = "*****";
string domain = "LDAP://****";
//Define LDAP Connection
string ABSAusername = "****";
string ABSApassword = "****";
string ABSAdomain = "LDAP://****";
//Create Directory Searcher
DirectoryEntry ldapConnection = new DirectoryEntry(domain,username,password);
ldapConnection.AuthenticationType = AuthenticationTypes.Anonymous;
DirectorySearcher ds = new DirectorySearcher(ldapConnection);
ds.Filter = "((EmploymentStatus=0))";
ds.SearchScope = System.DirectoryServices.SearchScope.Subtree;
//Create Directory Searcher
DirectoryEntry ABSAldapConnection = new DirectoryEntry(ABSAdomain, ABSAusername, ABSApassword);
ABSAldapConnection.AuthenticationType = AuthenticationTypes.Anonymous;
DirectorySearcher ABSAds = new DirectorySearcher(ABSAldapConnection);
ABSAds.Filter = "((&(EmploymentStatus=3)(EmploymentStatusDescription=Active))";
ABSAds.SearchScope = System.DirectoryServices.SearchScope.Subtree;
ds.PropertiesToLoad.Add("cn");
ds.PropertiesToLoad.Add ("uid");
ds.PropertiesToLoad.Add("sn");
ds.PropertiesToLoad.Add("PersonnelAreaDesc");
ds.PropertiesToLoad.Add("JobcodeID");
ds.PropertiesToLoad.Add("CostCentreID");
ds.PropertiesToLoad.Add("CostCentreDescription");
ds.PropertiesToLoad.Add ("givenName");
ds.PropertiesToLoad.Add ("EmploymentStatus");
ds.PropertiesToLoad.Add("EmploymentStatusDescription");
ABSAds.PropertiesToLoad.Add("uid");
ABSAds.PropertiesToLoad.Add("EmploymentStatus");
ABSAds.Sort = new SortOption("uid", SortDirection.Ascending);
ds.Sort = new SortOption("cn", SortDirection.Ascending);
SearchResultCollection absaUsers = ds.FindAll();
SearchResultCollection srcUsers = ds.FindAll();
sw.WriteLine("Action" + "," + "uid" + "," + "Business Area" + "," + "employeeNumber" + "," + "First Name" + "," + "Last Name" + "," + "JobCodeID" + "," + "costCentreID" + "," + "costCentreDescription" + "," + "FullName" + "," + "EmploymentStatus" + "," + "EmploymentStatusDescription" );
sw.WriteLine("");
foreach (SearchResult users in srcUsers)
{
string cn = users.Properties["cn"][0].ToString();
string sn = users.Properties["sn"][0].ToString();
string userID = users.Properties["uid"][0].ToString();
string description = users.Properties["PersonnelAreaDesc"][0].ToString();
// string jobCodeID = users.Properties["JobcodeID"][1].ToString();
string CostCentreID = users.Properties["costCentreID"][0].ToString();
string CostCentreDescription = users.Properties["CostCentreDescription"][0].ToString();
string givenName = users.Properties["givenName"][0].ToString();
string employmentStatus = users.Properties["EmploymentStatus"][0].ToString();
string EmploymentStatusDescription = users.Properties["EmploymentStatusDescription"][0].ToString();
foreach (SearchResult absaUser in absaUsers)
{
string absaUID = absaUser.Properties["uid"][0].ToString();
string absaEmploymentStatus = absaUser.Properties["EmploymentStatus"][0].ToString();
if (cn == absaUID)
{
if (absaEmploymentStatus == "3")
{
sw.WriteLine(cn);
}
}
}
}
sw.Flush();
sw.Close();
sw.Dispose();
}
}
I created two foreach loops, in the first loop i assigned variables to strings, and in the second foreach loop i do a comparison with an IF statement. What i want to do is: if the uid in one LDAP is equal to the uid in the other ldap, and if the status of the user in the 1st ldap = 0 but the status of the user in the 2nd ldap = 3: then i want to print out the users that match that criteria from the 1st ldap.
If you look through my code, am i doing somthing wrong? The output of the program currently is about 10 users that are duplicated atleast 100 times each.
Thanks in advance.
There are several things obviously wrong with this code....
1) First of all, you're creating the same search result twice:
SearchResultCollection absaUsers = ds.FindAll();
SearchResultCollection srcUsers = ds.FindAll();
So if that searcher finds 10 users, you have two collections of the same 10 users here.
2) You then used nested foreach loops, so you basically go through all results from the first collection, one by one, and for each of those entries, you enumerate the entire second collection - basically doing a cartesian product between the two collections. So that's why you end up with 10 x 10 = 100 users in the end.....
3) you seem to be missing some kind of a restriction/condition to pick out only those elements from your second/inner result set that match the outer/first result set in some way. As long as you don't have such a condition in place, you'll always get all the results from the second result set for each element of the first result set = a classic cartesian product. Somehow, you want to select only certain elements from the second set, based on something from the first result set......
You have missed break in the following place:
if (cn == absaUID && absaEmploymentStatus == "3")
{
sw.WriteLine(cn);
break;
}
Related
My problem in the title i have allcodes array and codes TextBox (kodTxtBox)
i will split textbox like line per element and querying all elements with for loop then
when i run it, it shows the query of only the last element of the allcodes array with the
messagebox, but the others go into else and giving error message box
some turkish words in my codes so.
aciklama = description
birim = monad
birimFiyat = Price per 1 unit
ürünler = products
ürünler.sipariskod = products.ordercode etc.
i did a lot of ways for this i used foreach all variables type is string
allCodes = kodTxtBox.Text.Split('\n');
for (int i = 0; i < allCodes.Length; i++)
{
queryString = "SELECT ürünler.siparisKod, ürünler.aciklama, ürünler.birim, ürünler.fGrup, ürünler.birimfiyat FROM ürünler WHERE (((ürünler.siparisKod)=\"" + allCodes[i] + "\"));";
using (OleDbCommand query = new OleDbCommand(queryString))
{
query.Connection = connection;
reader = query.ExecuteReader();
if (reader.Read())
{
MessageBox.Show(allCodes[i] + " Succesful");
var desc = reader["aciklama"].ToString();
var monad = reader["birim"].ToString();
var sellPrice = reader["birimFiyat"].ToString();
MessageBox.Show("Açıklama: " + desc + " Birim: " + monad + " Satış Fiyatı: " + sellPrice);
reader.Close();
}
else
{
MessageBox.Show("Hata");
}
}
}
I solved the problem by making a single query instead of multiple queries. I saved the values returned in each single query into a list and at the end I made the necessary for loop using the elements of the list
I have this code that works very well when I want to search JUST for the user's last name
public static string GetPhoneFromAD()
{
try
{
DirectoryEntry entryDomain = new DirectoryEntry("LDAP://" + domain);
DirectorySearcher ds = new DirectorySearcher(entryDomain);
string currentContextIdentity = System.Web.HttpContext.Current.User.Identity.Name;
string lastName = currentContextIdentity.Split(' ')[currentContextIdentity.Split(' ').Length - 1];
ds.Filter = "(sn=" + lastName + ")";
SearchResult sr = ds.FindOne();
string telephoneNumber = sr.Properties["telephoneNumber"][0].ToString();
return FormatPhone(telephoneNumber);
}
catch (Exception exception)
{
drmsda.InsertErrorlog("manage.aspx.cs", "Error in an attempt to get the phone number", exception.Source, exception.Message + " " + exception.StackTrace, "");
drmsda.sendErrorEmail("Error: SetPhone generated email", exception.Message);
return string.Empty;
}
}
However, if the user's last name is common, like Smith, I won't the right entry. Thus, I would like to add another criteria, such as first name. However, the query I came up with is not working. I did try the following
ds.Filter = "(givenName=" + firstName + "&sn=" + lastName + ")";
But that is not working, can somebody help?
Thank you in advance
LDAP filter syntax is a bit different to that; you want:
(&(givenName=joe)(sn=bloggs))
See here for many examples.
I have a Function that, when activated, iterates through the Crystal Report it is attached to, copies it to a pdf, then mails the entirety of it to a client once per identifying field.
They want to receive a pdf of records grouped by ID, for each ID in the Report, omitting some of a specific ID. I have no idea how to break the Report down into smaller Reports, though, or even where to begin if that's possible in the first place.
I am Creating Each Pdf for individual user and saving to my Google Driver also emailing using SendGrip Api.
I have used this code inside page.Aspx -> aspx.cs file.
//0. Here i am getting list users as an Object:
OpsManagementController OM = new OpsManagementController();
//1. Getting Users List:
var result = OM.UsersGetforInvoice();
//2. Creating folder for Invoices:
string folderName = #"D:\Google Drive\MonthlyInvoices";
string fileName = ("Invoices_" + DateTime.Now.ToString("yyyy-MM-dd").ToString());
string pathString = System.IO.Path.Combine(folderName, fileName);
System.IO.Directory.CreateDirectory(pathString);
string folderNameEmail = #"D:\Google Drive\MonthlyInvoices\Email";
string fileNameEmail = ("Invoices_" + DateTime.Now.ToString("yyyy-MM-dd").ToString());
string pathStringEmail = System.IO.Path.Combine(folderNameEmail, fileNameEmail);
System.IO.Directory.CreateDirectory(pathStringEmail);
//3. Generating invoices by user name:
for (int i = 0; i < result.UserDetail.Count; i++)
{
var userId = result.UserDetail[i].UserID;
var userEmail = result.UserDetail[i].Email;
var userName = result.UserDetail[i].FullName;
userName = userName.Replace(#"C\O", "CO");
userName = userName.Replace(#"C/O", "CO");
// Directories for reports:
var invoicePath = "D:/Google Drive/MonthlyInvoices/" + fileName + "/" + userId + " " + userName + ".pdf";
var invoicePath_email = "D:/Google Drive/MonthlyInvoices/Email/" + fileNameEmail + "/" + userId + " " + userName + ".pdf";
report2.SetParameterValue("UserID", result.UserDetail[i].UserID);
report2.ExportToDisk(ExportFormatType.PortableDocFormat, invoicePath);
// using sendgrip Api :
EmailUtils.SendEmail_Att(
new string[] { userEmail }, //TO : userEmail
new string[] { "email#gmail.com" }, //
invoiceSubject,
invoiceBody,
invoicePath_email
);
}
I am having an issue when attempting to create a new user in active directory. I followed the steps provided in this link for using PrincipalContext (with the exception that I am only doing one user at a time when they are hired and entered into the system and not multiple so no loop is required). I am also using a UserPrincipal Extender.
Here is the code that I have:
protected void CreateUserPE()
{
try
{
PrincipalContext userCtx = new PrincipalContext(ContextType.Domain, DomainFQDN, DomainFull);
string UserName = txtFirstName.Text.ToLower() + " " + txtLastName.Text.ToLower();
string password = "superSecretPassword";
UserPrincipalsEx newUser = new UserPrincipalsEx(userCtx, UserName, password, true);
newUser.SamAccountName = txtFirstName.Text.ToLower() + "." + txtLastName.Text.ToLower();
newUser.UserPrincipalName = txtFirstName.Text.ToLower() + "." + txtLastName.Text.ToLower() + "#rasm.com";
newUser.EmployeeId = txtEmpID.Text;
newUser.LastName = txtLastName.Text;
newUser.GivenName = txtFirstName.Text;
newUser.DisplayName = txtFirstName.Text + " " + txtLastName.Text;
newUser.Name = txtFirstName.Text + " " + txtLastName.Text;
newUser.SetPassword(password);
newUser.HomePostalAddress = txtAddress.Text + ", " + txtCity.Text + ", " + txtState.Text + ", " + txtZip.Text;
newUser.CountryName = txtCountry.Text;
newUser.HomePhone = txtHomePhone.Text;
newUser.MobilePhone = txtMobilePhone.Text;
newUser.DateOfBirth = txtDOB.Text;
newUser.EmergencyContact = txtEmergencyCnt.Text;
newUser.EmergencyPhone = txtContactPhone.Text;
newUser.Relationship = ddlRelationship1.SelectedItem.ToString();
newUser.EmergencyContact2 = txtEmergencyCnt2.Text;
newUser.EmergencyPhone2 = txtContactPhone2.Text;
newUser.Relationship2 = ddlRelationship2.SelectedItem.ToString();
newUser.EyeColor = ddlEyeColor.SelectedItem.ToString();
newUser.HairColor = ddlHairColor.SelectedItem.ToString();
newUser.Height = txtHeight.Text;
newUser.Weight = txtWeight.Text;
newUser.Gender = ddlGender.SelectedItem.ToString();
newUser.PersonalEmail = txtPersonalEmail.Text;
newUser.PassportExpires = txtPassportExp.Text;
newUser.HomeBase = ddlHomeStation.SelectedItem.ToString();
newUser.WorkLocation = txtWorkLocation.Text;
newUser.PID = txtPID.Text;
newUser.Team = txtTeam.Text;
newUser.Manager = "CN=" + txtSupervisor.Text + "," + DomainFull;
newUser.Title = ddlJobTitle.SelectedItem.ToString();
newUser.JobCode = txtJobCode.Text;
newUser.PLC = txtPLC.Text;
newUser.BPLC = txtBPLC.Text;
newUser.Specialty = txtSpecialty.Text;
newUser.Position = txtPosition.Text;
newUser.DateOfHire = txtDOH.Text;
newUser.DateOnContract = txtDOC.Text;
newUser.TaskOrder = ddlTaskOrder.SelectedItem.ToString();
newUser.Classification = ddlClass.SelectedIndex.ToString();
newUser.Section = txtSection.Text;
newUser.GatePass = txtGatePass.Text;
newUser.GatePassExpires = txtGatePassExp.Text;
newUser.WorkPhone = txtWorkPhone.Text;
newUser.CompanyEmail = txtCompEmail.Text;
newUser.aKOEmail = txtMilEmail.Text;
newUser.aKOSponsor = txtMilEmailSp.Text;
newUser.CACSponsor = txtCacSponsor.Text;
newUser.CACSponsorEmail = txtCacSponsorEmail.Text;
newUser.CacCardExpires = txtCacExpires.Text;
newUser.Enabled = true;
newUser.ExpirePasswordNow();
newUser.Save();
newUser.Dispose();
}
catch
{
}
}
The program goes all the way to newUser.Save() and then throws the following error in the catch statement:
System.DirectoryServices.AccountManagement.PrincipalOperationException was caught
HResult=-2146233087
Message=The attribute syntax specified to the directory service is invalid.
Source=System.DirectoryServices.AccountManagement
ErrorCode=-2147016693
StackTrace:
at System.DirectoryServices.AccountManagement.ADStoreCtx.Insert(Principal p)
at System.DirectoryServices.AccountManagement.Principal.Save()
at Personnel_Employee.CreateUserPE() in c:\inetpub\wwwroot\TestingFolder\Personnel\Add\Employee.aspx.cs:line 263
InnerException: System.DirectoryServices.DirectoryServicesCOMException
HResult=-2147016693
Message=The attribute syntax specified to the directory service is invalid.
Source=System.DirectoryServices
ErrorCode=-2147016693
ExtendedError=87
ExtendedErrorMessage=00000057: LdapErr: DSID-0C090D11, comment: Error in attribute conversion operation, data 0, v23f0
StackTrace:
at System.DirectoryServices.DirectoryEntry.CommitChanges()
at System.DirectoryServices.AccountManagement.SDSUtils.ApplyChangesToDirectory(Principal p, StoreCtx storeCtx, GroupMembershipUpdater updateGroupMembership, NetCred credentials, AuthenticationTypes authTypes)
InnerException:
Where am I going wrong.
You can not update an attribute with null or empty. I personaly dislike solutions with dummy values. If you are using the context principle just simply check for null or empty and dont update if its the case like:
if (!string.IsNullOrEmpty(txtbox.Text)){ newUser.attributeName = txtbox.Text}
If you are using an directory entry instead of an usercontext you can do something like this:
string adPath = "LDAP://server.domain.com/CN=John,CN=Users,dc=domain,dc=com";
DirectoryEntry userEntry = new DirectoryEntry(adPath);
if (txtBox.text == "")
{
userEntry.Properties["proppertyName"].Clear();
}
else if (!string.IsNullOrEmpty(txtBox.text))
{
userEntry.Properties[attribute.Key].Value = txtBox.text;
}
// dont do a thing when txtBox.Text is empty
It looks like more code but its much easier to make a foreachloop for it if you have a list with all attribute like:
private void UpdateEntryAttributes(DirectoryEntry entry, Dictionary<string, string> attributes)
{
foreach (KeyValuePair<string, string> attribute in attributes)
{
entry.Properties[attribute.Key].Value = attribute.Value;
if (attribute.Value == "")
{
entry.Properties[attribute.Key].Clear();
}
else if (!string.IsNullOrEmpty(attribute.Value))
{
entry.Properties[attribute.Key].Value = attribute.Value;
}
}
This can happen when attempting to write either a null or empty string to an AD field that prohibits it. An easy way to check whether this is the problem is to temporarily replace all such values with a dummy string (length > 0) and then run the code again. If that works, you can attempt a fix by changing the offending value--with AD, sometimes if null doesn't work, then an empty string will work, or vice versa.
I am new to stackoverflow as a member, although I follow this a lot :)
My code which connects to active directory to get the members of a functional group, gives me only 1490 odd members out of 1680 odd members actually in the list. I have searched a lot on Stackoverflow and on Internet, but I have not found out answer to why the code would result in incomplete list. Could, anyone please give me any pointers on this. Thanks :)
Here is the code which connects to the Active Directory to retrieve the data:
public static DataTable GetAdUsers(string configSection)
{
DataRow dr;
Hashtable ADGroups = (Hashtable)ConfigurationManager.GetSection(configSection);
string adGroup;
string adGroupDesc;
string sApplication;
string sLast_Login;
string sAccount_owner;
string sPath;
DataTable dt = new DataTable();
sApplication = "Application";
dt.Columns.Add(sApplication);
dt.Columns.Add("Profile", Type.GetType("System.String"));
dt.Columns.Add("Account Name", Type.GetType("System.String"));
sLast_Login = "Last Login";
dt.Columns.Add(sLast_Login);
sAccount_owner = "Account Owner";
dt.Columns.Add(sAccount_owner);
sPath = "Path";
dt.Columns.Add(sPath);
string domainName = "myDomain";
PrincipalContext pcRoot = new PrincipalContext(ContextType.Domain, domainName);
IDictionaryEnumerator adGroupEnumerator = ADGroups.GetEnumerator();
while (adGroupEnumerator.MoveNext())
{
adGroup = adGroupEnumerator.Key.ToString();
adGroupDesc = adGroupEnumerator.Value.ToString();
GroupPrincipal grp = GroupPrincipal.FindByIdentity(pcRoot, IdentityType.SamAccountName, adGroup);
System.DirectoryServices.DirectoryEntry de = (System.DirectoryServices.DirectoryEntry)grp.GetUnderlyingObject();
foreach (string sDN in de.Properties["member"])
{
System.DirectoryServices.DirectoryEntry deMember = new System.DirectoryServices.DirectoryEntry("LDAP://" + sDN.ToString());
try
{
dr = dt.NewRow();
string output1;
string subStringE1 = "DC=";
int length1 = de.Path.ToString().Length;
int length0 = de.Path.ToString().IndexOf(subStringE1);
string str1 = de.Path.ToString().Substring(length0, length1 - length0);
string subStringE2 = ",DC";
int length2 = str1.ToString().IndexOf(subStringE2);
output1 = str1.ToString().Substring(3, length2 - 3);
dr["Application"] = "Application";
dr["Profile"] = adGroupDesc;
string AccountName = deMember.Properties["samAccountName"].Value.ToString();
dr["Account Name"] = deMember.Properties["samAccountName"].Value.ToString();
dr["Last Login"] = "";
dr["Account Owner"] = deMember.Properties["givenName"].Value.ToString() + #"-" + deMember.Properties["sn"].Value.ToString();
string Path = output1 + #"\" + adGroup + #"\" + deMember.Properties["samAccountName"].Value.ToString();
Console.WriteLine(Path);
dr["Path"] = output1 + #"\" + adGroup + #"\" + deMember.Properties["samAccountName"].Value.ToString();
dt.Rows.Add(dr);
}
catch (Exception ex)
{
Console.WriteLine("Error occured for user name" + sDN + "\n" + ex.Message);
}
}
}
return dt;
}
}
Because of the default limit of SizeLimit, you're probably only getting around the 1,490 objects or so. To fix that you need to "page" through the results.
Just use the code referenced at Enumerating Members in a Large Group .
When there is the possibility that the result set that will be returned will contain more than 1000 items, you must use a paged search. Searches of Active Directory performed without paging are limited to returning a maximum of the first 1000 records. With a paged search, the result set is presented as individual pages, each containing a predetermined number of result entries. With this type of search, new pages of result entries are returned until the end of the result set is reached.
By default, the server that responds to a query request completely calculates a result set before returning data. In a large result set, this requires server memory as the result set is acquired, and network bandwidth when the large result is returned. Setting a page size allows the server to send the data in pages as the pages are being built. The client then caches this data and provides a cursor to the application level code.
Paging is set by defining how many rows the server calculates before the data is returned over the network to the client.
Found the answer by researching through and trying my luck.
Using Directory Entry was the problem, and did not ned to use Page Searching, although I tried the same.
The working code is as below:
GroupPrincipal grp = GroupPrincipal.FindByIdentity(pcRoot, adGroup); //fglbcmdolpctx
if (grp != null)
{
foreach (Principal p in grp.GetMembers(true))
{
try
{
dr = dt.NewRow();
dr["Application"] = "Commodities OpenLink";
dr["Profile"] = adGroupDesc;
string AccountName = p.SamAccountName.ToString().ToLower();
dr["Account Name"] = AccountName;
dr["Last Login"] = "";
string sLastName, sFirstName;
int iLastNameIndex0, iLastNameIndex1, iFirstNameIndex0, iFirstNameIndex1;
int lengthofString = p.Name.ToString().Length;
iLastNameIndex1 = p.Name.ToString().IndexOf(",");
if (iLastNameIndex1 == -1)
{
sLastName = "";
}
else
{
sLastName = p.Name.ToString().Substring(0, iLastNameIndex1);
}
iFirstNameIndex0 = p.Name.ToString().IndexOf(",");
iFirstNameIndex1 = p.Name.ToString().IndexOf(":");
if (iFirstNameIndex0 == -1 || iFirstNameIndex1 == -1)
{
sFirstName = p.Name.ToString();
sLastName = "";
}
else
{
sFirstName = p.Name.ToString().Substring(iFirstNameIndex0 + 1, iFirstNameIndex1 - iFirstNameIndex0 - 1);
}
sAccount_owner = sLastName + #"-" + sFirstName;
dr["Account Owner"] = sAccount_owner;
string sPath_Domain_Part;
string sFirstIndexofExtraction = "DC=";
int ilength_String = p.DistinguishedName.ToString().Length;
int iLenght_ExtractionPoint1 = p.DistinguishedName.ToString().IndexOf(sFirstIndexofExtraction);
string str1 = p.DistinguishedName.ToString().Substring(iLenght_ExtractionPoint1, ilength_String - iLenght_ExtractionPoint1);
string subStringE2 = ",DC";
int iLenght_ExtractionPoint2 = str1.IndexOf(subStringE2);
sPath_Domain_Part = str1.Substring(3, iLenght_ExtractionPoint2 - 3);
string sPath1 = sPath_Domain_Part + #"\" + adGroup + #"\" + p.SamAccountName.ToString();
dr["Path"] = sPath_Domain_Part + #"\" + adGroup + #"\" + p.SamAccountName.ToString();
dt.Rows.Add(dr);
}
catch (Exception ex)
{
Global.logfile.WriteLine("Error occured for user name" + adGroup + p.SamAccountName + "\n" + ex.Message);
}
}
}