I am trying to retrieve all DCs from domain and would also like to know if they are readable or writable.
To know if DC is readable or not, I need to read attribute primaryGroupID value for DC. Trying same in code below.
Code snippet -
var currDomain = Domain.GetCurrentDomain();
foreach (DomainController dc in currDomain.DomainControllers)
{
var dcName = dc.Name;
var de = dc.GetDirectoryEntry(); // DirectoryEntry represent DC
// Loads the values of the specified properties into the property cache.
de.RefreshCache(new[] {"primaryGroupID"});
if (de.Properties.Contains("primaryGroupID"))
{
int primaryGroupID;
var strPrimaryGroupID = Convert.ToString(de.Properties["primaryGroupID"][0]);
if (int.TryParse(strPrimaryGroupID, out primaryGroupID))
{
// RID for the "Read-only Domain Controllers" built-in group in Active Directory
// Writable Domain Controllers have primaryGroupID set to 516 (the "Domain Controllers" group).
dcName = string.Format(primaryGroupID == 521 ? "{0} (Read only)" : "{0} (Writable)", dc.Name);
}
}
}
However still property don't show up. I had confirmed from AD that the respective attribute exists for DC.
The default properties that show up are shown in screenshot below -
Do you have any clue why the property primaryGroupID is missing?
dc.GetDirectoryEntry() returns a server object, not the computer object of the DC. And primaryGroupID doesn't exist on server objects. But it does contain a reference to the computer object. Try this:
var dcDe = dc.GetDirectoryEntry(); // DirectoryEntry represent DC
var de = new DirectoryEntry("LDAP://" + dcDe.Properties["serverReference"].Value);
if (de.Properties.Contains("primaryGroupID"))
...
Related
I am accessing Active Directory. If I call it like this
DirectorySearcher srch = new DirectorySearcher(de);
//Filter to return only users and deleted users and not system accounts
srch.Filter = "(|(&(objectCategory=person)(objectClass=user)(sn=*))(&(isDeleted=TRUE)(objectClass=user)))";
srch.SearchScope = SearchScope.OneLevel;
srch.ExtendedDN = ExtendedDN.Standard;
srch.FindAll();
then it returns a list of users with some of the properties... I want to see the "whenChanged" property but when i try adding the line
srch.PropertiesLoad.Add("whenChanged");
then it doesn't return any users. Could this be due to deleted user's not having that property and that it can't uniformly apply all the properties so it returns 0 results? How can I view all the users, both deleted and active and see the "whenChanged" property for all even it results in a null
Several points:
To get deleted objects you need to set srch.Tombstone = true;
Deleted objects are stored under "CN=Deleted Objects,DC=domain,DC=com".
So to search for all users plus deleted objects, would better use domain root as search root and use SearchScope.Subtree as scope
Any attributes added to DirectorySearcher.PropertiesLoad should not remove any results.
This may due to reason other than srch.PropertiesLoad.Add("whenChanged");
Why put sn=* in search? this filter out users whose last name is not set.
Is this intended?
Tested following code that can get the users plus deleted user successfully, plus obtain the "whenChanged" property. Please give a try.
DirectoryEntry de = new DirectoryEntry("LDAP://domain.com/dc=domain,dc=com", "user", "pwd");
DirectorySearcher srch = new DirectorySearcher(de);
//Filter to return only users and deleted users and not system accounts
srch.Filter = "(|(&(objectCategory=person)(objectClass=user)(sn=*))(&(isDeleted=TRUE)(objectClass=user)))";
srch.SearchScope = SearchScope.Subtree;
srch.ExtendedDN = ExtendedDN.Standard;
srch.Tombstone = true;
srch.PropertiesToLoad.Add("whenChanged");
srch.PropertiesToLoad.Add("distinguishedName");
using (SearchResultCollection results = srch.FindAll())
{
foreach (SearchResult result in results)
{
string dn = result.Properties["distinguishedName"][0] as string;
Console.WriteLine("- {0}", dn);
ResultPropertyValueCollection prop = result.Properties["whenChanged"];
if (prop != null)
{
Console.WriteLine(" {0}", (DateTime)prop[0]);
}
}
}
I have a Name Picker on an XPage with a dataProvider dominoNABNamePicker with the addressBookSel = all-public and people and groups = true. With the database on a Domino server using the Notes Client it displays my local Names.nsf. If I open the DB in a brouse it selects the correct names.nsf from the server.
Can't figure out if this is the result of settings in my client, the server or XPages. Does the same thing on two different PC's.
I would think that the all-public would force it to open only public NABs but it does not appear so.
I asked the same question myself.
The answer, in the control add
addressBookDb="SERVER!!names.nsf"
From here.
Can I have the extlib name picker running in xPINC lookup the directory on the server?
After a fair bit of frustration I have this working for the Notes Client and the Web Client. Perhaps this is obvious to most of you, but it sure wasn't to me.
First on the Name Picker I created a namePickerAggregator. Then I added a dominoNABNamePicker
in the addressBookDb I put the following SSJS:
var server:String = #Name("[CN]",session.getCurrentDatabase().getServer());
var allNABs:Array = session.getAddressBooks().iterator();
var pubNABs = new Array;
var privNABs = new Array;
while (allNABs.hasNext()) {
var db:NotesDatabase = allNABs.next();
if (db.isPublicAddressBook()){
pubNABs.push(db.getFileName())
} else {
privNABs.push(db.getFileName())
}
db.recycle()
}
if (pubNABs[0] == ""){
return privNames[0];
break;
} else {
return server + "!!" + pubNABs[0];
break
}
I then added a second dominoNABNamePicker with the same block of code except the return is
if (pubNABs[1] != "") {
return server + "!!" + pubNABs[1];
break;
} else {
return "";
}
This code works for both the Notes Client and the Web client so I'm now a happy camper, unless I find a gotcha somewhere.
Here is what I eventually did. I set a limit on the maximum number of address books (not great but it works) of 4 you can create as many as you want. So I created a couple of sessionScope variable that I created in the after Page Loads event on the XPage. I used this formula.
var allNABs:Array = session.getAddressBooks().iterator();
var pubNABs = new Array;
var privNABs = new Array;
while (allNABs.hasNext()) {
var db:NotesDatabase = allNABs.next();
if (db.isPublicAddressBook()){
pubNABs.push(db.getFilePath())
} else {
privNABs.push(db.getFilePath())
}
db.recycle()
}
sessionScope.put("ssPublicNABs", pubNABs);
sessionScope.put("ssPrivateNABs", privNABs);
because I use several different Name Pickers on the same page I did not want to repeat having to cycle through the Address books.
Then I created 4 NamePicker controls and added 1, 2 , 3 and 4 dominoNABNamePickers providers to each of the successive controls. Then set the rendered property based on the number of public Address books so they would not blow up on me. The db name property on each of the providers is :
var server:String = #Name("[CN]",session.getCurrentDatabase().getServer());
var pubNABs:Array = sessionScope.get("ssPublicNABs");
return server + "!!" + pubNABs[0];
where pubNABs[n] returns the correct filePath for the NAB. It works well in both Notes Client and the Web.
Then to make it not blow up on a local disconnected replica I created 4 more controls and did the same thing but used the privNABs with appropriate rendered properties so that there is no conflict. Seems like the long way around and I'm sure that there is an easier way, but it works.
I'm using System.DirectoryServices.AccountManagement to query for a user and then find the groups for that user.
var _principalContext = new PrincipalContext(ContextType.Domain, domainAddress, adContainer, adQueryAccount, adQueryAccountPassword);
var user = UserPrincipal.FindByIdentity(_principalContext, IdentityType.SamAccountName, account);
var userGroups = user.GetGroups();
foreach (var group in userGroups.Cast<GroupPrincipal>())
{
//////////////////////////////////////////////////////
// getting the underlying DirectoryEntry shown
// to demonstrate that I can retrieve the underlying
// properties without the exception being thrown
DirectoryEntry directoryEntry = group.GetUnderlyingObject() as DirectoryEntry;
var displayName = directoryEntry.Properties["displayName"];
if (displayName != null && displayName.Value != null)
Console.WriteLine(displayName.Value);
//////////////////////////////////////////////////////
Console.WriteLine(group.DisplayName);// exception thrown here...
}
I can grab the underlying DirectoryEntry object and dump its properties and values but as soon as the GroupPrincipal.DisplayName property (or any property for that matter) is accessed, it throws the following exception:
"System.Runtime.InteropServices.COMException (0x8007200A): The
specified directory service attribute or value does not exist.\r\n\r\n
at System.DirectoryServices.DirectoryEntry.Bind(Boolean
throwIfFail)\r\n at
System.DirectoryServices.DirectoryEntry.Bind()\r\n at
System.DirectoryServices.DirectoryEntry.get_SchemaEntry()\r\n at
System.DirectoryServices.AccountManagement.ADStoreCtx.IsContainer(DirectoryEntry
de)\r\n at
System.DirectoryServices.AccountManagement.ADStoreCtx..ctor(DirectoryEntry
ctxBase, Boolean ownCtxBase, String username, String password,
ContextOptions options)\r\n at
System.DirectoryServices.AccountManagement.PrincipalContext.CreateContextFromDirectoryEntry(DirectoryEntry
entry)\r\n at
System.DirectoryServices.AccountManagement.PrincipalContext.DoLDAPDirectoryInitNoContainer()\r\n
at
System.DirectoryServices.AccountManagement.PrincipalContext.DoDomainInit()\r\n
at
System.DirectoryServices.AccountManagement.PrincipalContext.Initialize()\r\n
at System.DirectoryServices.Account
Management.PrincipalContext.get_QueryCtx()\r\n at
System.DirectoryServices.AccountManagement.Principal.HandleGet[T](T&
currentValue, String name, LoadState& state)\r\n at
System.DirectoryServices.AccountManagement.Principal.get_DisplayName()\r\n
at ConsoleApplication9.Program.Main(String[] args)"
Why would I be able to dump the raw properties of the underlying DirectoryEntry but not be able to call any of the properties directly on the GroupPrincipal? What would cause this exception? Note that this does not happen on the "Domain Users" group but the subsequent groups, it does...
I found the solution. If I pass the context to the GetGroups method, it works.
var user = UserPrincipal.FindByIdentity(_principalContext, IdentityType.SamAccountName, account);
var userGroups = user.GetGroups(_principalContext);
Apparently, this limits the groups retrieved to the domain associated with the context. Although this is not intuitive because the context was used to retrieve the user in the first place!!!
This leads me to believe there must be groups from other domains being returned previously and permissions were as such to prevent accessing that information.
Why are you using the .GetUnderlyingObject() call? Seems totally superfluous... just use the .SamAccountName property of the GroupPrincipal directly...
Try this:
foreach (var group in userGroups.Cast<GroupPrincipal>())
{
Console.WriteLine(group.SamAccountName);
Console.WriteLine(group.DisplayName);
Console.WriteLine(group.IsSecurityGroup);
}
Seems a lot easier - no?
My question is directly in relation to this one.
The accepted answer says "You also have to change state of the relation".
I use Model-First approach, and I don't have the foreign key in my entity. I only have the navigation property.
I Can change the state of the entities via DbEntityEntry, but I can't figure how to change the state of the relation itself. How can I access it?
Exemple of my code :
Building building = new Building() { Id = 1, Name = "modified" }; //Buiding 1 exists in DB
building.Adress = new Adress() { Id = 1, Road = "Sesame street" }; //Address 1 exists in DB
building.Adress.State = new State() { Id = 1 }; //State with Id 1 exists in DB
dbContext.Entry<Building>(building).State = EntityState.Modified;
dbContext.Entry<Adress>(building.Adress).State = EntityState.Modified;
//The state itself is not modified, but the relation between adress and state may do.
dbContext.Entry<State>(building.Adress.State).State = EntityState.Unchanged;
dbContext.SaveChanges();
This code pass without error, but the relation between Adress and State is never updated.
The Name of the building and the Street properties does.
Take a look at the ObjectStatementManager (and use ctrl + . to get the references for IObjectContextAdapter).
var unchangedItems = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Unchanged);
var addedItems = ((IObjectContextAdapter)dbContext).ObjectContext.ObjectStateManager.GetObjectStateEntries(EntityState.Added);
It also specifically has ChangeRelationshipState which sounds appropriate in your scenario.
I need to evaluate an user's password expiration time against an Active Directory.
I'm using Android and Unboundid sdk. I can successfully connect to server using this code
final SocketFactory _socket_factory;
final SSLUtil _ssl_util = new SSLUtil(new TrustAllTrustManager());
try {
_socket_factory = _ssl_util.createSSLSocketFactory();
}
catch (Exception e) {
Log.e(LOG_TAG, "*** Unable to initialize ssl", e);
return null;
}
LDAPConnectionOptions _ldap_connection_options = new LDAPConnectionOptions();
_ldap_connection_options.setAutoReconnect(true);
_ldap_connection_options.setConnectTimeoutMillis(30000);
_ldap_connection_options.setFollowReferrals(false);
_ldap_connection_options.setMaxMessageSize(1024*1024);
LDAPConnection _ldap_connection = new LDAPConnection(_socket_factory, _ldap_connection_options, _host, _port);
BindRequest _bind_request = new SimpleBindRequest(_username, _password);
BindResult _bind_result = _ldap_connection.bind(_bind_request);
I retreive user attributes using a search
Filter _filter = Filter.create("(userPrincipalName=lorenzoff)");
SearchRequest _search_request = new SearchRequest(_server._base_dn, SearchScope.SUB, _filter);
But how can I read the domain's attribute 'maxPwdAge'? I can see it in among the domain attributes...
I need it to evaluate the remaining days until user's password expires.
I had the same issue and found a solution. the idea is simple you have to access the base DN and get that attribute:
SearchRequest _search_request = new SearchRequest(_server._base_dn,
SearchScope.BASE, "(objectClass=*)","maxPwdAge");
with this you should get the result with that attribute, if you get SearchRequest.ALL_USER_ATTRIBUTES you will have all the attributes shown on your screenshot.
That attribute is common for all users, the next thing you need to do is to search your specific user as you where doing before an get the attribute pwdLastSet, as you would expect it has the timestamp of the last time the user changed his password.
now is simple, you need to find the expiration date with the last time the user change it, and the password age
hope it helps
If maxPwdAge is an "operational" attribute, it must be explicitly requested as part of your search request. "User" attributes are returned (as permissions permit), but "operational" attributes must be explicitly requested. To request maxPwdAge create your request as follows:
SearchRequest _search_request = new SearchRequest(_server._base_dn,
SearchScope.SUB, _filter,"maxPwdAge");
The SeachRequest constructor actually accepts a variable length list of attribute types also:
SearchRequest _search_request = new SearchRequest(_server._base_dn,
SearchScope.SUB,_filter,"maxPwdAge","minPwdAge",
SearchRequest.ALL_USER_ATTRIBUTES);
requests maxPwdAge, minPwdAge, and all other user attributes. To request all operational attributes, use SearchRequest.ALL_OPERATIONAL_ATTRIBUTES.