Spring LDAP search with organisational units returns zero results - active-directory

I'm working on a plain java command line software which performs a recursive LDAP search with Spring LDAP, starting from a specified group and searching all the users from the specified groups and subgroups.
The search fails to find anything if the group distinguished name contains organisational units (=ou), but works in other cases.
Here is the short version of implementation, recursion omitted:
private void searchLdapGroup(List<UserDTO> users, LdapTemplate ldapTemplate, String groupName) {
// recursion guard omitted
String base = groupName.substring(groupName.indexOf(',') + 1);
AndFilter filter = new AndFilter().and(new EqualsFilter("objectclass", "group")).and(new EqualsFilter("memberof", groupName));
List<String> subgroups = ldapTemplate.search(base, filter.encode(), new GroupNameMapper());
// recursive calls for subgroups omitted
getAllUsers(users, ldapTemplate, groupName, base);
}
private void getAllUsers(List<UserDTO> users, LdapTemplate ldapTemplate, String groupName, String base) {
AndFilter filter = new AndFilter().and(new EqualsFilter("objectclass", "person")).and(new EqualsFilter("memberof", groupName));
// Paged search omitted.
List<UserDTO> result = ldapTemplate.search(base,filter.encode(),new UserAttributesMapper());
users.addAll(result);
}
The GroupNameMapper returns distinguishedName as Strings and UserAttributesMapper returns user objects from different attributes such as sAMAccountName and givenName.
The code (with recursion) finds all the 36 users in the first test group, where the specified group is like:
CN=import_users,CN=Users,DC=example,DC=test,DC=org
in the same exact test environment it returns zero persons and subgroups when the group distinguished name contains one or more organisational units, such as
CN=import_users,OU=testou,DC=example,DC=test,DC=org
This can't be due to wrong group distinguished name, "memberof" not working or group containing no users, since i tested lookup:
String[] test = (String[])ldapTemplate.lookup("CN=import_users,OU=testou,DC=example,DC=test,DC=org", new ContextMapper() {
public Object mapFromContext(Object ctx) {
DirContextAdapter adapter = (DirContextAdapter) ctx;
return adapter.getStringAttributes("Member");
}
});
which finds
CN=John Doe,CN=Users,DC=example,DC=test,DC=org
and lookup for the user John Doe
String[] test = (String[])ldapTemplate.lookup("CN=John Doe,CN=Users,DC=example,DC=test,DC=org", new ContextMapper() {
public Object mapFromContext(Object ctx) {
DirContextAdapter adapter = (DirContextAdapter) ctx;
return adapter.getStringAttributes("memberof");
}
});
gives results:
CN=import_users,OU=testou,DC=example,DC=test,DC=org
CN=import_users,CN=Users,DC=example,DC=test,DC=org
How come the search does not find anything when organisational units are involved?
Library user:
spring-ldap-core - 2.0.4.RELEASE

The devil is in the details:
The member of the group CN=import_users,OU=testou,DC=example,DC=test,DC=org is
CN=John Doe,CN=Users,DC=example,DC=test,DC=org
But you appear to be searching for users under
OU=testou,DC=example,DC=test,DC=org
That is, it appears all users are under CN=Users,DC=example,DC=test,DC=org, but when you are actually searching for users you assume they are placed relative to the group.

Related

Salesforce, Apex, SOQL, SELECT with INCLUDES

What is wrong at this code in Apex?
String states = 'California,New York';
List<Account> lstACC = [SELECT Id, Name, BillingState FROM Account WHERE BillingState INCLUDES (:states) LIMIT 10];
In Developer Console is an Error: "BillingState FROM Account WHERE BillingState INCLUDES (:states)^ERROR at Row:1:Column:50 includes or excludes operator only valid on multipicklist field".
The text of the error shows the problem:
includes or excludes operator only valid on multipicklist field
BillingState is not a multi-select picklist. Use IN rather than INCLUDES to match against the collection.
Note additionally that a comma-separated string is not a valid collection to match against. Create a List<String> or Set<String> to use in your matching condition.
The right solution:
Set<String> setStates = new Set<String>();
setStates.add('California');
setStates.add('New York');
List<Account> lstACC = [SELECT Id, Name, BillingState
FROM Account
WHERE BillingState IN :setStates
LIMIT 10];
Wrong:
setStates: {'California','New York'}
Right:
setStates: {California,New York}
Apostrophes are in addition.
OR
String states = 'California,New York';
List<String> listStates = states.split(',');
List<Account> lstACC = [SELECT Id, Name, BillingState
FROM Account
WHERE BillingState IN :listStates
LIMIT 10];
Wrong:
String states = '\'California\',\'New York\'';
Right:
String states = 'California,New York';
SOQL injection is in addition.

Get list of parent records from multilevel SOQL query

I'm trying to combine 3 separate SOQL queries into one, but still end up with 3 separate lists for ease of use and readability later.
List<Object__c> objectList = [SELECT Name, Id, Parent_Object__r.Name, Parent_Object__r.Id,
(SELECT Name, Id FROM Child_Objects__r)
FROM Object__c];
I know I can get a list of child objects thus:
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
}
How would I go about adding the Parent_Object__c records to their own list?
I'm assuming a map could be used to deal with duplicates, but how do I get this Parent_Object__c data into that map?
You are basically there.
All lookup fields are available in your example as object.Parent_Object__r. Use a Set to natively avoid duplicates. No deduping required on your part!
Set<Parent_Object__c> parentObjectSet = new Set<Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
parentObjectSet.add(object.Parent_Object__r);
}
Edit:
As per #eyescream (trust him!) you are indeed better off with a map to avoid duplicates.
So the above code would just be slightly different:
Map<Id, Parent_Object__c> parentObjectMap = new Map<Id, Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
if (object.Parent_Object__r != null) {
parentObjectMap.put(object.Parent_Object__r.Id, object.Parent_Object__r);
}
}

LDAP Ambiguous Name Resolution (ANR) equivalent on Microsoft Graph

I'm building a tool that can target both on-prem Active Directory and Azure AD that, amongst other things, searches user objects.
For on-prem, I can do an LDAP search using ambiguous name resolution with a query like this:
(anr=searchstring*)
Is there an equivalent filter string for the Microsoft Graph?
The equivalent to {string}* is the OData startsWith({property},'{string}') Filter parmeter:
/v1.0/users?$filter=startsWith(displayName,'Adele')
Note that the Microsoft Graph currently only supports a subset of OData query parameters, particularly when it comes to AAD filters:
The following $filter operators are not supported for Azure AD resources: ne, gt, ge, lt, le, and not.
Also, Microsoft Graph does not support contains or endswith string functions in any workload.
You can try test your queries this using Graph Explorer.
Okay, so after some trial & error, I managed to replicate Ambiguous Name Resolution using the filter syntax. It's not an exact match because not all attributes are searchable, but I guess it will have to do.
Here's my helper class I created for the job.
public static class QueryHelper
{
public static string GenerateAnrFilter(string query)
{
if (string.IsNullOrEmpty(query))
return query;
var tokens = query.Split(' ');
var filterClauses = new List<string>();
if (tokens.Count() > 1)
{
var otherTokens = string.Join(" ", tokens.Skip(1));
string nameFilter1 = $"({generateFilterClause(tokens[0], "givenName")} and {generateFilterClause(otherTokens, "surname")})";
filterClauses.Add(nameFilter1);
string nameFilter2 = $"({generateFilterClause(otherTokens, "givenName")} and {generateFilterClause(tokens[0], "surname")})";
filterClauses.Add(nameFilter2);
filterClauses.Add(generateFilterClause(query, "displayName"));
//filterClauses.Add(generateFilterClause(query, "officeLocation")); // not supported for filter
filterClauses.Add(generateMultiValueFilterClause(query, "proxyAddresses"));
filterClauses.Add(generateFilterClause(query, "userPrincipalName"));
//filterClauses.Add(generateFilterClause(query, "onPremisesSamAccountName")); // not supported for filter
filterClauses.Add(generateFilterClause(query, "mail"));
filterClauses.Add(generateFilterClause(query, "mailNickName"));
}
else
{
filterClauses.Add(generateFilterClause(query, "displayName"));
filterClauses.Add(generateFilterClause(query, "givenName"));
filterClauses.Add(generateFilterClause(query, "surname"));
//filterClauses.Add(generateFilterClause(query, "officeLocation")); // not supported for filter
filterClauses.Add(generateMultiValueFilterClause(query, "proxyAddresses"));
filterClauses.Add(generateFilterClause(query, "userPrincipalName"));
//filterClauses.Add(generateFilterClause(query, "onPremisesSamAccountName")); // not suported for filter
filterClauses.Add(generateFilterClause(query, "mail"));
filterClauses.Add(generateFilterClause(query, "mailNickName"));
}
var fullQuery = string.Join(" or ", filterClauses);
return fullQuery;
}
private static string generateMultiValueFilterClause(string query, string attributeName)
{
return $"{attributeName}/any(a:startswith(a,'{query}'))";
}
private static string generateFilterClause(string query, string attributeName)
{
return $"startsWith({attributeName},'{query}')";
}
}

Find all IRIs used in ontology which are not entities?

Is there any straight-forward way in OWLAPI to find all IRIs used in an ontology which have not been identified as Entities i.e. have not been declared and are not used in a context which would allowed them to be identified as a specific entity type? Hoping for something analogous to OWLOntology.signature(), didn't see anything.
An example of that situation appears in BFO 2.0 (http://purl.obolibrary.org/obo/bfo.owl) :
<rdf:Description rdf:about="http://example.com/bfo-spec-label">
<obo:IAO_0000119>Person:Alan Ruttenberg</obo:IAO_0000119>
</rdf:Description>
Here http://example.com/bfo-spec-label is just a "bare" IRI of unknown entity type and thus does not appear in the ontology signature.
Couldn't find any elegant way to find all of these bare IRI's but these can be found by looking in all places they could possibly occur. A simple method would look like:
private List findBareIRIs(OWLOntology onto) {
List bares = new ArrayList();
// bare IRIs can occur as AnnotationSubjects, AnnotationObjects or the domain/range of AnnotationProperties
List<OWLAnnotationAssertionAxiom> asserts = OWLAPIStreamUtils.asList(onto.axioms(AxiomType.ANNOTATION_ASSERTION));
List<OWLAnnotationPropertyDomainAxiom> domains = OWLAPIStreamUtils.asList(onto.axioms(AxiomType.ANNOTATION_PROPERTY_DOMAIN));
List<OWLAnnotationPropertyRangeAxiom> ranges = OWLAPIStreamUtils.asList(onto.axioms(AxiomType.ANNOTATION_PROPERTY_RANGE));
//check the subject and values of each AnnotationAsertion
for (OWLAnnotationAssertionAxiom ax : asserts) {
OWLAnnotationSubject subj = ax.getSubject();
OWLAnnotationValue value = ax.getValue();
if (subj.isIRI()) {
bares.add((IRI) subj);
}
if (value.isIRI()) {
bares.add((IRI) value);
}
}
// check the domain and ranges of each AnnotationProperty
for (OWLAnnotationPropertyDomainAxiom ax : domains) {
bares.add(ax.getDomain());
}
for (OWLAnnotationPropertyRangeAxiom ax : ranges) {
bares.add(ax.getRange());
}
return bares;
}

Apex - Retrieving Records from a type of Map<SObject, List<SObject>>

I am using a lead map where the first id represents an Account ID and the List resembles a list of leads linked to that account such as: Map<id, List<Id> > leadMap = new Map< id, List<id> >();
My question stands as following: Knowing a Lead's Id how do I get the related Account's Id from the map. My code looks something like this, The problems is on the commented out line.
for (Lead l : leads){
Lead newLead = new Lead(id=l.id);
if (l.Company != null) {
// newLead.Account__c = leadMap.keySet().get(l.id);
leads_to_update.add(newLead);
}
}
You could put all lead id and mapping company id in the trigger then get the company id
Map<string,string> LeadAccountMapping = new Map<string,string>();//key is Lead id ,Company id
for(Lead l:trigger.new)
{
LeadAccountMapping.put(l.id,l.Company);
}
//put the code you want to get the company id
string companyid= LeadAccountMapping.get(l.id);
Let me make sure I understand your problem.
Currently you have a map that uses the Account ID as the key to a value of a List of Lead IDs - So the map is -> List. Correct?
Your goal is to go from Lead ID to the Account ID.
If this is correct, then you are in a bad way, because your current structure requires a very slow, iterative search. The correct code would look like this (replace your commented line with this code):
for( ID actID : leadMap.keySet() ) {
for( ID leadID : leadMap.get( actId ) ) {
if( newLead.id == leadID ) {
newLead.Account__c = actId;
leads_to_update.add(newLead);
break;
}
}
}
I don't like this solution because it requires iterating over a Map and then over each of the lists in each of the values. It is slow.
If this isn't bulkified code, you could do a Select Query and get the Account__c value from the existing Lead by doing:
newLead.Account__c = [ SELECT Account__c FROM Lead WHERE Id = :l.id LIMIT 1];
However, this relies on your code not looping over this line and hitting a governor limit.
Or you could re-write your code soe that your Map is actually:
Map<ID, List<Leads>> leadMap = Map<ID, List<Leads>>();
Then in your query where you build the map you ensure that your Lead also includes the Account__c field.
Any of these options should work, it all depends on how this code snippet in being executed and where.
Good luck!

Resources