"I want to update the values of several properties of an Active Directory user at the same time,
I try the following method but it does not pass ..."
public void SetAdInfo(string objectFilter,
Dictionary<string, object> objectName,
string ldapPath)
{
string connectionPrefix = "LDAP://" + ldapPath;
DirectoryEntry entry = new DirectoryEntry(connectionPrefix);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
mySearcher.Filter = "(cn=" + objectFilter + ")";
mySearcher.PropertiesToLoad.Add("" + objectName + "");
SearchResult result = mySearcher.FindOne();
if (result != null)
{
DirectoryEntry entryToUpdate = result.GetDirectoryEntry();
foreach (var prop in objectName)
{
entryToUpdate.Properties[prop.Key].Value = prop.Value;
entryToUpdate.CommitChanges();
}
}
entry.Close();
entry.Dispose();
mySearcher.Dispose();
}
This is likely your problem right here:
mySearcher.PropertiesToLoad.Add("" + objectName + "");
The objectName parameter is a Dictionary<string, object>, but you're using it like a string. When you do that, .NET calls the .ToString() method on the object, which will end up being something you don't want, like "System.Collections.Generic.Dictionary`2[System.String,System.Object]".
That's likely what's messing up your search.
But you aren't reading the properties of the search results anyway. You're just calling .GetDirectoryEntry() on each result. So you don't even need to set PropertiesToLoad. So just take that line out.
If you did want to read each property from the search results, you need to add each property individually, so you would need to loop through the Dictionary and add each Key to the PropertiesToLoad:
foreach (var prop in objectName) {
mySearcher.PropertiesToLoad.Add(prop.Key);
}
Related
I have a list of Users being returned from AD and I need to filter them to just return the Active users. This is my code, but it's not returning any users. After extensive googling, I'm at a loss as to what's missing;
public static List<Models.ToolUser> ActiveUsers()
{
int unlimitedAccess;
//string userAccountControl;
string listEntry;
string fName;
string lName;
string unlimitedAccessGroup;
//This is getting the List of Users that I need to filter
List<Models.ToolUser> activeUserList = UIDal.GetFullWindowsUserList();
try
{
string filter = "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=2))";
string[] propertiesToLoad = new string[1] { "name" };
using (DirectoryEntry de = GetDirectoryEntryWithGc())
using (DirectorySearcher searcher = new DirectorySearcher(de, filter, propertiesToLoad))
using (SearchResultCollection results = searcher.FindAll())
{
foreach (SearchResult result in results)
{
unlimitedAccess = 0;
fName = result.Properties["givenName"][0].ToString();
lName = result.Properties["sn"][0].ToString();
listEntry = fName + " " + lName;
var name = result.Properties["sAMAccountName"][0].ToString();
var u = new ToolUser
{
ToolUserId = 0,
DomainAccount = result.Properties["sAMAccountName"][0].ToString(),
FirstName = fName,
LastName = lName,
LoginId = "pc-" + result.Properties["sAMAccountName"][0].ToString(),
UnlimitedAccess = unlimitedAccess > 0,
};
activeUserList.Add(u);
}
}
}
catch
{
}
return activeUserList;
}
Empty catch blocks are the devil. You should at least log the exception before continuing.
In this case, your empty catch block is hiding what's really going on. You're getting an "Index was out of range" exception here:
fName = result.Properties["givenName"][0].ToString();
Because result.Properties["givenName"] is an empty collection (there is no element at index 0). That's happening because of this:
string[] propertiesToLoad = new string[1] { "name" };
You are telling the search to only return the name attribute for the objects found, but then you go on to use givenName, sn and sAMAccountName. You need to tell it to return those attributes if you intend to use them:
string[] propertiesToLoad = new string[3] { "givenName", "sn", "sAMAccountName" };
That said, givenName and sn are not required attributes. If those attributes are empty on any of the accounts found, then they will not appear in the Properties collection at all and you will run into the same exception again. So you should test that those attributes are actually there before trying to use them. For example, this will check and set the variables to an empty string if the attribute doesn't exist:
fName = result.Properties.Contains("givenName") ? result.Properties["givenName"][0].ToString() : "";
lName = result.Properties.Contains("sn") ? result.Properties["sn"][0].ToString() : "";
My current code is looping through a list containing saved strings in an array. Currently it looks for all strings in that array. I want to change this so that it only goes through (searching, looking) for strings within "log[1]"
Sorry, i dont know the word for "log[1]". Im new to programming. Keep reading and i think you will understand.
This is how i want to do it:
foreach (string[] item[1] in loggbok)
item[1] being log[1]. Number 1 is very important because I want to search only within log[1].
This is my current code for saving the whole array in my list:
List<string[]> loggbok = new List<string[]> { };
string[] log = new string[3]; //date, title, post
DateTime date = DateTime.Now;
log[0] = "\n\tDate: " + date.ToLongDateString() + " Kl: " + date.ToShortTimeString();
Console.WriteLine(log[0]);
Console.Write("\tTitle: ");
log[1] = "\tTitle: " + Console.ReadLine();
Console.Write("\tPost: ");
log[2] = "\tPost: " + Console.ReadLine();
loggbok.Add(log);
log = new string[3];
I save "log[1],log[2],log[3]"
The following code i want to make a search function which goes through my list and recognise all the strings within log[1] aka titles. If a string title is containing the users keyword all logs should join and the log will be printed.
As of now. I solved this by searching through all logs(1,2,3). This means that my program is searching currently for strings within (titles, date, posts). This makes it so that you can search for messages or "post" when i want the user to be restricted by only searching for titles.
So i thought maby if in my foreach loop i make "item" to "item[1]". Will that make my code to only look for "log[1]". I did not get that far though becouse writing "item[1]" is invalid syntax.
Current search function:
string key;
Console.Write("\n\tSearch: ");
key = Console.ReadLine();
//Searching through all log[] in loggbok.
//I want to change this line to item[1]
foreach (string[] item in loggbok)
{
//goes deeper and looks for all strings within log[].
foreach (string s in item)
{
//if a string is found containing key word, this block will run.
if (s.Contains(key))
{
foundItem = true;
Console.WriteLine(String.Join("\r\n", item));
index++;
}
}
}
Probably you can do it like this:
var result = loggbok.FirstOrDefault(x=> x.Any(s=> s.Contains(key));
Console.WriteLine(result?? "No record found");
You don't even need to loop, so what you need to do is retrieve the item from loggbok by the index.
// assign loggbokx of index 1, to variable item.
string[] item = loggbok[1];
// item will then have the 2nd (index=1) logbook.
// Note that index starts from 0.
// If you want to have the first one, then it should be loggbox[0]
// to make it even simpler you can write
// var item = loggbok[1];
// and the rest is the same...
//goes deeper and looks for all strings within log[].
foreach (string s in item)
{
//if a string is found containing key word, this block will run.
if (s.Contains(key))
{
foundItem = true;
Console.WriteLine(String.Join("\r\n", item));
index++;
}
}
Let's do it right!
Create a model class for your log:
class LogEntry
{
public DateTime Date { get; set; }
public string Title { get; set; }
public string Post { get; set; }
public override string ToString()
{
return "Date: " + Date.ToLongDateString() + " Kl: " + Date.ToShortTimeString()
+ "\tTitle: " + Title + "\tPost: " + Post;
}
}
Now we can comfortably use this model.
Let's populate the list with more records:
List<LogEntry> loggbok = new List<LogEntry>();
for (int i = 0; i < 5; i++)
{
LogEntry entry = new LogEntry();
entry.Date = DateTime.Now;
entry.Title = "title" + i;
entry.Post = "post" + i;
loggbok.Add(entry);
}
Let's print it:
foreach (var entry in loggbok)
Console.WriteLine(entry);
Due to the ToString method overload output looks out nice.
Let's find something:
string key = "title3";
var found = loggbok.Find(log => log.Title == key);
Console.WriteLine("Found:\n" + found);
We can use different methods of the List class, and LINQ extension methods.
If you need to save your data to a file and then read them from there, you can use json serialization.
For example, let's use the JavaScriptSerializer (don't forget to add a reference to the assembly):
JavaScriptSerializer jss = new JavaScriptSerializer();
// Save
File.WriteAllText("test.txt", jss.Serialize(loggbok));
// Load
loggbok = jss.Deserialize<List<LogEntry>>(File.ReadAllText("test.txt"));
This is the solution if anyone finds it intressting.
foreach (string[] item in loggbok)
{
foreach (string s in item)
{
//This was the magic line.
string searchTitle = item[1].ToLower();
if (searchTitle.Contains(titleKey.ToLower()))
{
Console.WriteLine("\n\tSearch hit #" + index);
foundItem = true;
Console.WriteLine(String.Join("\r\n", item));
index++;
break;
}
}
}
We use the secretary property of Active Directory, which is a multivalued DN property. I want to efficiently find all the groups for which a given user is a listed secretary. What is the best way to perform this query in C#/.NET?
This code will return a list of sAMAccountNames of groups in context's OU that include distinguishedName as one of the secretaries listed:
var filter = "(&(objectClass=group)(secretary=" + distinguishedName + "))";
PrincipalContext context = new PrincipalContext(...); // place to search from
var up = new GroupPrincipal(context);
var list = new List<string>();
using (var searcher = new PrincipalSearcher(up))
{
var ds = searcher.GetUnderlyingSearcher() as DirectorySearcher;
// if you only want to search in a single OU (as defined by 'context')
ds.SearchScope = SearchScope.OneLevel;
ds.Filter = filter;
ds.PropertiesToLoad.Add("sAMAccountName");
var results = ds.FindAll();
foreach (SearchResult r in results)
{
var name = r.GetDirectoryEntry().Properties["sAMAccountName"].Value as string;
list.Add(name);
}
}
I want to assign all my User properties from active directory into a List< User > properties by using LINQ.
For that I have tried some code but I am not able to assign Manager attribute on List User Manager property.
Because , assign the Manager attribute value from Active directory we need to search the manager user on active directory and based on the search result we need to assign manager attribute value to List of User.Manager property.
Here is my Code,
public List< User > GetADUsers()
{
DirectorySearcher searcher = new DirectorySearcher(ADEntry);
searcher.Filter = "(&(objectClass=user)objectCategory=person)";
SearchResultCollection resultCol = searcher.FindAll();
return
(from serachresult in resultCol.OfType<SearchResult>()
let result = serachresult.GetDirectoryEntry()
result.Properties["sAMAccountName"].Value != null
select new User
{
GivenName = result.Properties.Contains("givenname") ?
Convert.ToString(result.Properties["givenname"][0]) : "",
SurName = result.Properties.Contains("sn") ?
Convert.ToString(result.Properties["sn"][0]) : "",
Location = result.Properties.Contains("physicalDeliveryOfficeName") ?
Convert.ToString(result.Properties["physicalDeliveryOfficeName"][0]) : "",
Manager= ?// Here I need to assign the manager attribute from active directory
}
).ToList();
}
By using below mentioned code, I am taking the manager attribute from Active Directory by using directory entry object. I want to place this code on above mentioned LINQ query
on Manager property. How can we achieve this inside of LINQ Select clause.
DirectoryEntry DomainRoot = AD.GetDirectoryEntry();
using (DirectorySearcher Search = new DirectorySearcher())
{
Search.SearchRoot = DomainRoot;
Search.Filter = "(&(distinguishedName=" +
Convert.ToString(result.Properties["manager"][0]) + "))";
SearchResult Result = Search.FindOne();
if (Result != null)
{
DirectoryEntry Manager = Result.GetDirectoryEntry();
//This Manager.Name value I have assign above mentioned LINQ
user.Manager = Manager.Name.Substring(3);
Manager.Close();
}
}
Please help me to assign the manager name on the LINQ query Manager property place! Thanks.
Try to move your manager evaluation to a new function that takes 'result' and returns Manager name.
And then call it from your link statement: Manager = GetManagerName(result)
public string GetManagerName(DirectoryEntry dirEntry)
{
DirectoryEntry DomainRoot = AD.GetDirectoryEntry();
using (DirectorySearcher search = new DirectorySearcher())
{
search.SearchRoot = DomainRoot;
search.Filter = "(&(distinguishedName=" + Convert.ToString(dirEntry.Properties["manager"][0]) + "))";
SearchResult result = search.FindOne();
if (result != null)
{
using (DirectoryEntry mgr = result.GetDirectoryEntry())
{
return mgr.Name.Substring(3);
}
}
return string.Empty;
}
}
I have the code below which is intended to build a dictionary object with the following construction Dictionary<string<Dictionary<string, string>. For some reason each time I add an item to it the key text is correct but the value (dictionary) overwrites the previous one. This is probably better explained like this:
Iteration 1
key1, dictionary1
Iteration 2
key1, dictionary2
key2, dictionary2
Iteration 3
key1, dictionary3
key2, dictionary3
key3, dictionary3
What is causing this and how can I fix this code to stop it overwriting the dictionary in each entry?
QueryNameUserQueryString = new Dictionary<string, string>();
DialectDictionary = new Dictionary<string, Dictionary<string, string>>();
while (dataBaseConnection.NextRecord())
{
if (QueryNameUserQueryString != null)
{
QueryNameUserQueryString.Clear();
}
string dialect = dataBaseConnection.GetFieldById (0);
//If no dialect then carry out next iteration
if (String.IsNullOrEmpty (dialect))
{
continue;
}
try
{
dataBaseConnection2.ExecutePureSqlQuery ("SELECT * FROM " + sqlFactoryTable + " WHERE SQL_FACTORY_DIALECT = '" + dialect + "'");
}
catch
{
dataBaseConnection.Close();
dataBaseConnection2.Close();
throw;
}
//Do we have query strings for this dialect?
if (!dataBaseConnection2.HasRows())
{
continue;
}
//loop through query strings
while (dataBaseConnection2.NextRecord())
{
string queryName = dataBaseConnection2.GetFieldById (2);
string queryString = dataBaseConnection2.GetFieldById (3);
string user = dataBaseConnection2.GetFieldById (4);
//create composite key for dictionary
string compositeKey = dialect + "," + queryName + "," + user;
if (QueryNameUserQueryString != null)
{
//Construct our query string dictionary
QueryNameUserQueryString.Add (compositeKey, queryString);
}
}
//If we have a query string dictionary
if (QueryNameUserQueryString != null)
{
//Ensure m_dialect dictionary is not null
if (DialectDictionary != null)
{
//Construct our dictionary entry for this dialect
DialectDictionary.Add (dialect, QueryNameUserQueryString);
}
}
}
}
You seem to be using the same instance of QueryNameUserQueryString on every iteration. When it's added to the DialectDictionary, it's added as a reference - not a copy of the original.
To "properly" solve the issue, I would move the declaration of your QueryNameUserQueryString variable inside the while-scope. That way you would make sure that it can only exist in the scope of a single iteration, not across several. When it's added to the DialectDictionary, the reference lives on in that dictionary and you're safe to leave the scope.
You are using same instance of QueryNameUserQueryString everytime. Replace
QueryNameUserQueryString.Clear();
with
QueryNameUserQueryString = new Dictionary<string, string>();