I have to query a domain controller for the enabled state of a specific account. I have to do this with LDAP because the machine making the query is not joined to the domain.
My code is as follows:
public async Task<bool> IsUserEnabled(string samAccountName)
{
var server = "[OMITTED]";
var username = "[OMITTED]";
var password = "[OMITTED]";
var domain = "[OMITTED]";
var query = $"(&(objectCategory=person)(SAMAccountName={samAccountName}))";
var credentials = new NetworkCredential(username, password, domain);
var conn = new LdapConnection(server);
try
{
conn.Credential = credentials;
conn.Timeout = new TimeSpan(0, 5, 0);
conn.Bind();
var domain_parts = domain.Split('.');
if (domain_parts.Length < 2)
{
throw new ArgumentException($"Invalid domain name (name used: \"{domain}\").");
}
var target = $"dc={domain_parts[0]},dc={domain_parts[1]}";
var search_scope = SearchScope.Subtree;
var search_request = new SearchRequest(target, query, search_scope, null);
var temp_response = await Task<DirectoryResponse>.Factory.FromAsync(
conn.BeginSendRequest,
(iar) => conn.EndSendRequest(iar),
search_request,
PartialResultProcessing.NoPartialResultSupport,
null);
var response = temp_response as SearchResponse;
if (response == null)
{
throw new InvalidOperationException("LDAP server answered NULL.");
}
var entries = response.Entries;
var entry = entries[0].Attributes["userAccountControl"];
var values = entry.GetValues(typeof(byte[])).First();
var output = (byte[])values;
var result = false;
return result;
}
catch (LdapException lex)
{
throw new Exception("Error querying LDAP server", lex);
}
catch (Exception ex)
{
throw new Exception("Unknown error", ex);
}
finally
{
conn.Dispose();
}
}
In the lines
var values = entry.GetValues(typeof(byte[])).First();
var output = (byte[])values;
I get a byte array with what I think is the current user's flags, but I don't know how to process this data in order to know if the user is enabled or not.
Most advice I found is that I should convert this to an integer and OR-it with 2 in order to know, but Convert.ToInt32(output) throws an exception (Unable to cast object of type 'System.Byte[]' to type 'System.IConvertible'.) and using BitConverter.ToInt32(output, 0) also throws an exception (Destination array is not long enough to copy all the items in the collection. Check array index and length.)
One thing I noticed is that the array is 3 bytes long, and I don't know if that's right.
Ive queried 2 users: one (we know this one is enabled in the AD) returns a [53, 49, 50] array, and the other one (this one is disabled) returns [53, 52, 54].
Where do I go from here?
AFAIK the userAccountControl attribute is a 4-byte integer value (a bit mask), so why would you try and convert this into a byte array at all?
var entry = entries[0].Attributes["userAccountControl"];
// return True if the UF_ACCOUNT_DISABLE flag (bit no. 1) is not set
// meaning the user is Enabled
return (entry & 2) == 0;
See userAccountControl
Related
Facing Arguments exception error on retrieving data from list of a query using c# in a cross platform xamarin project (ios and android). I am trying to retrieve data from easy tables on azure (cloud). here's my code
try
{
var client = new MobileServiceClient("https://eg.azurewebsites.net");
IMobileServiceTable<Reg> regTable = client.GetTable<Reg>();
Reg reg = new Reg();
string imail = uemail2.Text.ToString();
// This query to seach email
var email1 = await regTable
.Where(Reg => Reg.email.ToString() == imail)
.Select(email => email.Text.ToString())
.ToListAsync();
notice3.Text = email1[0].ToString();
}
catch (ArgumentException ex)
{
notice.Text = "Arguments Error" + ex.Message + ex.StackTrace + ex.HelpLink + ex.Source;
}
ERROR DETAILS
Index was out of range. Must be non-negative and less than the size of
the collection. Parameter name: index at
System.ThrowHelper.ThrowArgumentOutofRange_IndexException....
...//continued ex.Source = mscorlib
Thanks in Advance
My Updated Query:
public async Task<ObservableCollection<Reg>> GetTodoItemsAsync(bool syncItems = false)
{
var client = new MobileServiceClient("https://eg.azurewebsites.net");
IMobileServiceTable<Reg> regTable = client.GetTable<Reg>();
Reg reg = new Reg();
string imail= uemail2.Text.ToString();
IEnumerable<Reg> items = await regTable
.Where(email=> email.Text.ToString() == imail)
.ToEnumerableAsync();
return new ObservableCollection<Reg>(items);
}
private async void v_OnClicked(object sender, EventArgs e)
{
var client = new MobileServiceClient("https://eg.azurewebsites.net");
IMobileServiceTable<Reg> regTable = client.GetTable<Reg>();
Reg reg = new Reg();
string imail = uemail2.Text.ToString();
await GetTodoItemsAsync();
items email3 = new items();
vnotice2.Text = email3.ToString();
}
It seems the email1 is empty, so it's unable to find first element in the list and cuasing index out of exception.
You should validate the list before accessing any elements of that list.
if(email1.Count > 0)
notice3.Text = email1[0].ToString();
Possible reason for getting empty email1, might be the string comparison in where clause, you should ignore the case and compare the strings unless you are sure that both parameters in comparison have same case. Also if you want to fetch only one email then you should use FirstOrDefaultAsync. The modified query will look like below.
notice3.Text = await regTable
.Where(Reg => Reg.email.Equals(imail, StringComparison.OrdinalIgnoreCase))
.Select(email => email.Text.ToString()).FirstOrDefaultAsync<string>()
I am trying to perform paginated search on Active Directory using System.DirectoryServices.Protocols.PageResultRequestControl.
I do get the search results in pages, however, the searchResponse that I get does NOT have the correct TotalCount for total number of pages.
Is it not supported? Or am I missing something here?
This is sample code that I have used in order to implement above. I am using System.DirectoryServices.Protocols to query Active Directory.
When PageResultRequestControl is added with page number, everything works perfectly except for totalSize.
For example, in this code
LdapConnection connection = new LdapConnection(ldapDirectoryIdentifier, credential);
SearchRequest sr = new SearchRequest("", "(displayName=*)", System.DirectoryServices.Protocols.SearchScope.Subtree, new[] { "displayName"});
PageResultRequestControl pr = new PageResultRequestControl(50);
SearchOptionsControl so = new SearchOptionsControl(SearchOption.DomainScope);
sr.Controls.Add(pr);
sr.Controls.Add(so);
SearchResponse searchResponse;
while (true)
{
searchResponse = (SearchResponse)connection.SendRequest(sr);
if (searchResponse.Controls.Length != 1 || !(searchResponse.Controls[0] is PageResultResponseControl))
{
totalPageCount = 0;
return null;
}
PageResultResponseControl pageResponse = (PageResultResponseControl)searchResponse.Controls[0];
totalPageCount = pageResponse.TotalCount;
if (pageResponse.Cookie.Length == 0)
{
break;
}
else
{
pageRequest.Cookie = pageResponse.Cookie;
}
}
As documentation says, the TotalCount property contains the estimated result set count (https://technet.microsoft.com/en-us/library/system.directoryservices.protocols.pageresultresponsecontrol.totalcount)
This is my code to login to MongoDB by using MongoDB Authentication Mechanisms.
try
{
var credential = MongoCredential.CreateMongoCRCredential("test", "admin", "123456");
var settings = new MongoClientSettings
{
Credentials = new[] { credential }
};
var mongoClient = new MongoClient(settings);
var _database = mongoClient.GetDatabase("test");
var collection = _database.GetCollection<Test>("book");
var filter = new BsonDocument();
var document = collection.Find(new BsonDocument()).ToList();
}
catch (Exception ex)
{
}
When we put wrong username/password in the Credential, how to check the login result? Currently I can't check it, I have to wait to collection.Find().ToList() throw an TimeoutException , and in this context it's authentication failed. We must make a CRUD to check the authentication result (by catching TimeoutException). It's not a good manner to check login status.
And when we put right username/password to authenticate, how to check the account role in this database?
Looking at the source code for the C# MongoDB client, the MongoClient constructors do not throw any connectivity-related exceptions. It's only when an application uses the MongoClient to perform some a on the MongoDB server that an exception will be thrown. However, as you discovered, that exception is a generic time-out exception indicating that the driver failed to find a suitable server. As the exception itself does contain details regarding the failure, you can use that information to create a method like the one below to check if you can run a dummy command against the database. In this method I have reduced all the time out values to one second:
public static void CheckAuthentication(MongoCredential credential, MongoServerAddress server)
{
try
{
var clientSettings = new MongoClientSettings()
{
Credentials = new[] {credential},
WaitQueueTimeout = new TimeSpan(0, 0, 0, 1),
ConnectTimeout = new TimeSpan(0, 0, 0, 1),
Server = server,
ClusterConfigurator = builder =>
{
//The "Normal" Timeout settings are for something different. This one here really is relevant when it is about
//how long it takes until we stop, when we cannot connect to the MongoDB Instance
//https://jira.mongodb.org/browse/CSHARP-1018, https://jira.mongodb.org/browse/CSHARP-1231
builder.ConfigureCluster(
settings => settings.With(serverSelectionTimeout: TimeSpan.FromSeconds(1)));
}
};
var mongoClient = new MongoClient(clientSettings);
var testDB = mongoClient.GetDatabase("test");
var cmd = new BsonDocument("count", "foo");
var result = testDB.RunCommand<BsonDocument>(cmd);
}
catch (TimeoutException e)
{
if (e.Message.Contains("auth failed"))
{
Console.WriteLine("Authentication failed");
}
throw;
}
}
As per your comment you can query roles for a given user, using the snippet below:
var mongoClient = new MongoClient(clientSettings);
var testDB = mongoClient.GetDatabase("test");
string userName = "test1";
var cmd = new BsonDocument("usersInfo", userName);
var queryResult = testDB.RunCommand<BsonDocument>(cmd);
var roles = (BsonArray)queryResult[0][0]["roles"];
var result = from roleDetail in roles select new {Role=roleDetail["role"].AsBsonValue.ToString(),RoleDB=roleDetail["db"].AsBsonValue.ToString()};
So here is the code using S.DS.P to get all users very quickly in pages of 500 at a time..
public List<AllAdStudentsCV> GetUsersDistinguishedNamePagedResults( string domain, string distinguishedName )
{
try
{
NetworkCredential credentials = new NetworkCredential( ConfigurationManager.AppSettings["AD_User"], ConfigurationManager.AppSettings["AD_Pass"] );
LdapDirectoryIdentifier directoryIdentifier = new LdapDirectoryIdentifier( domain + ":389" );
List<AllAdStudentsCV> users = new List<AllAdStudentsCV>();
using (LdapConnection connection = new LdapConnection(directoryIdentifier, credentials))
{
string filter = "(&(objectClass=user)(objectCategory=person))";
string baseDN = ConfigurationManager.AppSettings["AD_DistinguishedName"];
string[] attribArray = {"name", "sAMAccountName", "objectGUID", "telexNumber", "HomePhone"};
List<SearchResultEntry> srList = PerformPagedSearch(connection, baseDN, filter, attribArray);
if (srList.Count == 0) return null;
foreach (SearchResultEntry entry in srList)
{
<...snip a bunch of code to filter out bad users by CN...>
users.Add( user );
}
catch ( Exception ex )
{
throw;
}
}
}
}
return users;
}
catch ( Exception ex )
{
throw;
}
}
private List<SearchResultEntry> PerformPagedSearch( LdapConnection connection, string baseDN, string filter, string[] attribs )
{
List<SearchResultEntry> results = new List<SearchResultEntry>();
SearchRequest request = new SearchRequest(
baseDN,
filter,
System.DirectoryServices.Protocols.SearchScope.Subtree,
attribs
);
PageResultRequestControl prc = new PageResultRequestControl(500);
//add the paging control
request.Controls.Add(prc);
int pages = 0;
while (true)
{
pages++;
SearchResponse response = connection.SendRequest(request) as SearchResponse;
//find the returned page response control
foreach (DirectoryControl control in response.Controls)
{
if (control is PageResultResponseControl)
{
//update the cookie for next set
prc.Cookie = ((PageResultResponseControl) control).Cookie;
break;
}
}
//add them to our collection
foreach (SearchResultEntry sre in response.Entries)
{
results.Add(sre);
}
//our exit condition is when our cookie is empty
if ( prc.Cookie.Length == 0 )
{
Trace.WriteLine( "Warning GetAllAdSdsp exiting in paged search wtih cookie = zero and page count =" + pages + " and user count = " + results.Count );
break;
}
}
return results;
}
It works perfectly on DEV and on Prod, but suddenly stopped working on the QA webserver when it talks to the QA AD server. it only returnes one page and then stops.
If I point DEV to the QA AD server it works correctly...
It was working before Feb 2012, last time I tested in QA, and definitely was broken in place by March 7, 2012
Can anyone think of anything that would cause this behavior? perhaps a windows update? I've had one jack this product up before...
I'm reasonably convinced that it's not the code or the configuration...as it works on so many other combinations... it's netowrk/securiyt/os related.. but I can't figure out what changed.
Any Help is appreicated
Had the exact same issue where no pages were returned after the first one.
Here is what I found to fix the problem:
PageResultRequestControl pageRequestControl = new PageResultRequestControl(500);
SearchOptionsControl soc = new SearchOptionsControl(System.DirectoryServices.Protocols.SearchOption.DomainScope);
request.Controls.Add(pageRequestControl);
request.Controls.Add(soc);
No idea what the SearchOptionsControl does, but since I added this, AD returns all the expected objects.
This line solves the issue (connection is LdapConnection) ->
connection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
https://social.msdn.microsoft.com/Forums/vstudio/en-US/17957bb2-15b4-4d44-80fa-9b27eb6cb61f/pageresultrequestcontrol-cookie-always-zero?forum=csharpgeneral
I am programming a client program that calls a webmethod but when I get the return data there are missing values on some of the fields and objects.
The webmethod in turn is calling a WCF method and in the WCF method the return data is fine. But when it is passing to the webservice the return data is missing.
Is there any way to fix this problem?
This is my client code calling the webservice:
ReLocationDoc query = new ReLocationDoc();
query.PerformerSiteId = 1;
query.PerformerUserId = 1;
query.FromStatus = 10;
query.ToStatus = 200;
ReLocationDoc doc = new ReLocationDoc();
ServiceReference1.QPSoapClient service = new QPSoapClient();
try {
service.GetRelocationAssignment(query, out doc);
string test = doc.Assignment.Id.ToString();
} catch(Exception ex) {
MessageBox.Show(ex.Message);
}
The webmethod code is here:
[WebMethod]
return m_reLocationClient.GetRelocationAssignment(query, out reLocationDoc);
}
And at last the WCF code:
public ReLocationResult GetRelocationAssignment(ReLocationDoc query, out ReLocationDoc reLocationDoc) {
try {
LOGGER.Trace("Enter GetRelocationAssignment().");
ReLocationResult result = reLocationCompactServiceClient.GetRelocationAssignment(out reLocationDoc, query);
if(reLocationDoc.Assignment == null || reLocationDoc.Assignment.CurrentStatus == STATUS_FINISHED) {
ReLocationDoc newQuery = new ReLocationDoc();
newQuery.Assignment = new AssignmentDoc();
newQuery.Assignment.EAN = DateTime.Today.ToString();
newQuery.PerformerSiteId = QPSITE;
newQuery.PerformerUserId = QPUSER;
reLocationDoc.AssignmentStatus = m_settings.ReadyStatus; ;
result = reLocationCompactServiceClient.CreateReLocationAssignment(out reLocationDoc, newQuery);
}
return result;
} finally {
LOGGER.Trace("Exit GetRelocationAssignment().");
}
}
The GetRelocationAssignment:
public ReLocationResult GetRelocationAssignment(ReLocationDoc query, out ReLocationDoc reLocationDoc) {
try {
LOGGER.Trace("Enter GetRelocationAssignment().");
ReLocationDoc doc = new ReLocationDoc();
ReLocationResult result = new ReLocationResult();
new Database(Connection).Execute(delegate(DBDataContext db) {
User user = GetVerifiedUser(db, query, MODULE_ID);
SiteModule siteModule = SiteModule.Get(db, query.PerformerSiteId, MODULE_ID);
Status status = Status.Get(db, query.FromStatus, query.ToStatus, 0);
Status startStatus = Status.Get(db, query.FromStatus, 0);
Status endStatus = Status.Get(db, query.ToStatus, 0);
IQueryable<Assignment> assignments = Assignment.GetAssignmentsWithEndStatus(db, siteModule, endStatus);
assignments = Assignment.FilterAssignmentStartStatus(assignments, startStatus);
foreach(Assignment assignment in assignments) {
LOGGER.Debug("Handling assignment: " + assignment.Id);
result.Status = true;
AssignmentDoc assignmentDoc = FillAssignmentDoc(assignment);
//ReLocationDoc doc = new ReLocationDoc();
AssignmentStatus sts = assignment.AssignmentStatus.OrderByDescending(ass => ass.Id).First();
assignmentDoc.CurrentStatus = sts.Status.Zone;
Status currentStatus = sts.Status;
IList<Item> items = assignment.Items.ToList();
IList<ItemDoc> itemDocs = new List<ItemDoc>();
foreach(Item item in items) {
ItemDoc itemDoc = FillItemDoc(item);
ItemDetail itemDetail;
if(ItemDetail.TryGet(db, item.Id, out itemDetail)) {
ItemDetailDoc itemDetailDoc = FillItemDetailDoc(itemDetail);
itemDoc.Details = new ItemDetailDoc[1];
Event eEvent = null;
if(Event.GetEvent(db, itemDetail, currentStatus, out eEvent)) {
EventDoc eventDoc = FillEventDoc(eEvent);
itemDetailDoc.Events = new EventDoc[1];
if(eEvent.LocationId.HasValue) {
Location location = null;
if(Location.TryGet(db, eEvent.LocationId.Value, out location)) {
eventDoc.Location = new LocationDoc();
eventDoc.Location = FillLocationDoc(location, db);
}
}
itemDetailDoc.Events[0] = eventDoc;
}
itemDoc.Details[0] = itemDetailDoc;
}
itemDocs.Add(itemDoc);
}
assignmentDoc.Items = itemDocs.ToArray();
doc.Assignment = assignmentDoc;
}
}, delegate(Exception e) {
result.Message = e.Message;
});
reLocationDoc = doc;
return result;
} finally {
LOGGER.Trace("Exit GetRelocationAssignment().");
}
}
In all this code the return data is fine. It is loosing data only when passing to the webmetod.
Enter code here.
Also, the ordering of the XML tags in the message makes difference - I had a similar problem about maybe two years ago, and in that case parameter values were dissappearing during transmission because the sending part ordered the tags differently than what was defined in the schema.
Make surethe XML tags are being accessed with the same casing at either end. if the casing is not the same then the value won't be read.
You should check it all message are sending back from your webservice. Call your webservice manually and check its response.
If all data is there, probably your webservice reference is outdated; update it by right-clicking your webservice reference and choose "Update"
If your data don't came back, your problem is probably related to webservice code. You should check your serialization code (if any) again, and make sure all returned types are [Serializable]. You should check if all return types are public as it's mandatory for serialization.
As noted per John Saunders, [Serializable] isn't used by XmlSerializer.