Product Information
Search Operation in LDAP Adapter in SAP Cloud Platform Integration
LDAP is lightweight directory access protocol. A directory is a location on the network that helps us locate some information. LDAP is designed to access large set of data fast hence improving the performance. LDAP is widely used for authentication for storing users and their data.
LDAP is used in Microsoft’s Active Directory (AD) and in open source tools like Open LDAP. In the examples mentioned below I will be using both Active Directory and Open LDAP for your disposal.
In SAP Cloud Platform Integration(CPI), we have the LDAP Receiver Adapter for making calls to your LDAP server. We support operations Insert, Modify, Delete and Search. The operations Insert and Modify are explained in this post and Delete operation is pretty straightforward, if you provide the Distinguished Name of the entity and that will be deleted.
In this blog post, we will be focusing on only the Search operation and its use cases. We have also assumed that you are aware of the cloud connector configuration and use of the virtual host as the address of the LDAP server in the integration flow.
Search Operation Configuration
When using LDAP Adapter version 1.3, you should be able to see the ‘Search’ in the operation drop down
Base DN
Entity from where you will begin the search. This is not a mandatory field, but we highly suggest using this to refine your search. In some LDAP serves, there is a default base DN and other servers mandate this.
Scope
Target portion that is defined to find the potential matches. There are 3 kinds of scopes:
- Object Specifies that only the base DN will be considered
- One Level – Specifies that the immediate level after the base DN be considered
- Subtree – Specifies the entire subtree after the base DN to be considered
Search Filter
Criteria which should qualify to fulfill the search
Output Type
Result of search operation, can be perceived as either XML data or as JNDI Attributes
- XML – Output will be in XML structure. Below is the sample response structure for the XML Output Type. Notice that now there is a new <Entry> tag which is used to differentiate between the result entries. <DistinguishedName> and <ObjectClass> will be populated only if they have been selected. You can also use the same payload to do Insert or Modify operations provided that there is just one <Entry> because these operation insert/update only one entity at one time.
<Schema>
<Entry>
<DistinguishedName>...</DistinguishedName>
<ObjectClass>...</ObjectClass>
<Attributes>
<Attribute1>...</Attribute1>
<Attribute2>...</Attribute2>
.
.
<AttributeN>...</AttributeN>
</Attributes>
</Entry>
.
.
<Entry>
<DistinguishedName>...</DistinguishedName>
<ObjectClass>...</ObjectClass>
<Attributes>
<Attribute1>...</Attribute1>
<Attribute2>...</Attribute2>
.
.
<AttributeN>...</AttributeN>
</Attributes>
</Entry>
</Schema>
- Java (JNDI) Attributes – This will set the output body as List<javax.naming.directory.Attributes>. Each entry will be a part of the list. You should ideally use the script step after to make manipulation to the body according to your usecase.
Attributes
The list of attributes you want to fetch in the search. All attributes will be returned if nothing is entered. This is case insensitive. If you have entered an incorrect attribute, the search will not fail, the result will just not have that attribute.
Size limit
Maximum number of entries to be returned from the search. This must be numeric and zero signifies no size limit i.e all entries will be returned.
Timeout
Maximum time in minutes that the search can take. Zero implies that the client will wait indefinitely for the result.
Performing Search
Unlike other LDAP operation like insert and update that are limited to just one entity at one time, the search operation can return multiple entities based on the criteria mentioned by you. In this section, we will perform some basic searches to get you started.
Sample Integration Flow
The integration flow used in this blog post is timer triggered and gets the data from the LDAP server and attaches it to the message processing log as an attachment.
Example Scenarios
As mentioned earlier I will be using Active Directory as well as Open LDAP servers in this section. The data and schema maintained in both are different. This is done just so that you can have an overview of how search can be performed.
Disclaimer: The same result can be achieved in many ways, below are examples which you can refer to.
Active Directory Examples
- Get names of all ‘Users’ entity
The processing tab of the LDAP Receiver Adapter would look like below. I am selecting the root of users as the base DN with my search criteria ‘objectClass=user’ and selecting only the name attribute.
Response
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Schema>
<Entry>
<Attributes>
<name>Administrator</name>
</Attributes>
</Entry>
<Entry>
<Attributes>
<name>Guest</name>
</Attributes>
</Entry>
<Entry>
<Attributes>
<name>John</name>
</Attributes>
</Entry>
..
..
</Schema>
- Getting all details of disabled Users whose CN contains ‘John’
Use * as a wildcard for such filter operations and & operand ensures both conditions are true to qualify. The attributes field is empty to view all attributes.
Response
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Schema>
<Entry>
<objectClass>top;person;organizationalPerson;user</objectClass> <distinguishedName>CN=john,CN=Users,DC=XXXXXX,DC=XXXXXX,DC=sap,DC=corp</distinguishedName>
<Attributes>
<proxyAddresses>abc@sap.com;test@gmail.com</proxyAddresses>
<sAMAccountType>805306368</sAMAccountType>
<primaryGroupID>513</primaryGroupID>
<badPasswordTime>0</badPasswordTime> <objectCategory>CN=Person,CN=Schema,CN=Configuration,DC=XXXXXX,DC=XXXXXX,DC=sap,DC=corp</objectCategory>
<mail>john.doe@sap.com</mail>
<cn>john</cn>
<userAccountControl>514</userAccountControl>
<telephoneNumber>2343434</telephoneNumber>
<dSCorePropagationData>16010101000000.0Z</dSCorePropagationData>
<codePage>0</codePage>
<whenChanged>20191219091709.0Z</whenChanged>
<whenCreated>20191219091709.0Z</whenCreated>
<pwdLastSet>0</pwdLastSet>
<logonCount>0</logonCount>
<accountExpires>9223372036854775807</accountExpires>
<lastLogoff>0</lastLogoff>
<objectGUID>c5142903-68fb-4dab-9f47-b10da758e707</objectGUID>
<lastLogon>0</lastLogon>
<uSNChanged>259902</uSNChanged>
<uSNCreated>259901</uSNCreated>
<objectSid>S-1-5-21-3722266703-2459750493-1115606453-17015</objectSid>
<countryCode>0</countryCode>
<sAMAccountName>john</sAMAccountName>
<instanceType>4</instanceType>
<badPwdCount>0</badPwdCount>
<name>john</name>
</Attributes>
</Entry>
<Entry>
<objectClass>top;person;organizationalPerson;user</objectClass> <distinguishedName>CN=johnny,CN=Users,DC=XXXXXX,DC=XXXXXX,DC=sap,DC=corp</distinguishedName>
<Attributes>
<sAMAccountType>805306368</sAMAccountType>
<primaryGroupID>513</primaryGroupID>
<badPasswordTime>0</badPasswordTime> <objectCategory>CN=Person,CN=Schema,CN=Configuration,DC=XXXXXX,DC=XXXXXX,DC=sap,DC=corp</objectCategory>
<mail>johnny.doe01@sap.com</mail>
<cn>johnny</cn>
<userAccountControl>514</userAccountControl>
<codePage>0</codePage>
<whenChanged>20191219091734.0Z</whenChanged>
<whenCreated>20191219091734.0Z</whenCreated>
<pwdLastSet>0</pwdLastSet>
<logonCount>0</logonCount>
<accountExpires>9223372036854775807</accountExpires>
<lastLogoff>0</lastLogoff>
<objectGUID>e8e94bd0-73c9-44fc-8382-229b25773621</objectGUID>
<lastLogon>0</lastLogon>
<uSNChanged>259905</uSNChanged>
<uSNCreated>259904</uSNCreated>
<objectSid>S-1-5-21-3722266703-2459750493-1115606453-17016</objectSid>
<countryCode>0</countryCode>
<sAMAccountName>johnny</sAMAccountName>
<instanceType>4</instanceType>
<badPwdCount>0</badPwdCount>
<name>johnny</name>
</Attributes>
</Entry>
</Schema>
- Searching with ObjectGUID with Java (JNDI) Attributes return type
For ObjectGuid: 2ac32557-be63-407e-a428-02341fa39b7f you will have to convert it to escaped hexadecimal bytes to use it in search which becomes \57\25\c3\2a\63\be\7e\40\a4\28\02\34\1f\a3\9b\7f
Response
// String representation of the list
[{distinguishedname=distinguishedName: CN=hci1,CN=Users,DC= XXXXXX,DC= XXXXXX,DC=sap,DC=corp}]
Example script to interpret the response
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.List;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.NamingEnumeration;
def Message processData(Message message) {
//Body
List<Attributes> body = message.getBody();
// Iterate over the entities
for(Attributes attributes : body){
NamingEnumeration enumeration = attributes.getAll();
// Iterate over the attributes of the entity
while (enumeration.hasMore()) {
BasicAttribute attribute = (BasicAttribute) enumeration.next();
// Attribute Key
String attributeName = attribute.getID();
// Attribute Value
String attributeValue = (String) attribute.get();
def messageLog = messageLogFactory.getMessageLog(message);
if(messageLog != null){
messageLog.addAttachmentAsString(attributeName, attributeValue, "text/xml");
}
}
}
}
Open LDAP Examples
- Get 5 women of ‘OrganizationalPerson’ who reside in Bangalore
Here we have 3 conditions to fulfil with objectClass to identify the category, address to look for Bangalore and title to look for women. Size limit is 5 to imply maximum of 5 employees to fetch who qualify these conditions
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Schema>
<Entry>
<Attributes>
<name>Annie Benitha</name>
<age>28</age>
<title>Miss</title>
<registeredAddress>Richmond Road, Bangalore</registeredAddress>
</Attributes>
</Entry>
<Entry>
<Attributes>
<name>Sadiya Kauser</name>
<age>29</age>
<title>Mrs</title>
<registeredAddress>Tippsandra Colony, Bangalore</registeredAddress>
</Attributes>
</Entry>
<Entry>
<Attributes>
<name>Deepa U</name>
<age>31</age>
<title>Mrs</title>
<registeredAddress>HSR Layout, Bangalore</registeredAddress>
</Attributes>
</Entry>
<Entry>
<Attributes>
<name>Mahashwetha Rao</name>
<age>39</age>
<title>Ms</title>
<registeredAddress>Ganganagar,2nd cross,Sector 8, Bangalore</registeredAddress>
</Attributes>
</Entry>
<Entry>
<Attributes>
<name>Aliya Amreen</name>
<age>35</age>
<title>Mrs</title>
<registeredAddress>HRBR Layout Bangalore</registeredAddress>
</Attributes>
</Entry>
</Schema>
- Getting multi values attributes
Some attributes have multiple values in CPI, we differentiate them with a ;(semicolon), for example objectClass.
Response
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Schema>
<Entry>
<objectClass>top;person;company</objectClass>
</Entry>
<Entry>
<objectClass>top;person;company</objectClass>
</Entry>
</Schema>
Summary
Now that you know how easy it is to use search in the LDAP adapter.Go ahead and try it yourself. I would also suggest to get rid of the script step where you have manually tried searching, use the adapter instead, to control all your configurations.
Feel free to write a comment for feedback. Looking forward to hearing from you! 🙂
Hi Sana,
This blog was right on time. Nicely written and covered different search filters.
I have tried the search LDAP option and it works fine just as mentioned.
Is there any way that we can assign parameter in Search Filter box and pass the required value from message mapping to search criteria for dynamic search operation/lookup?
Thanks
Praveen.
Hi Praveen, Yes it is possible.
You can give something like as follows:
(&(ObjectClass=User)(userPrincipalName=${property.email}))
Make sure that email parameter is assigned with value from incoming xml in previous content modifier.
All the best!
Thanks Mani!
You can not only have dynamic values with properties with ${property.<propertyName>} but also using headers with ${header.<headerName>}
Thank you Mani and Sana.
I could add both Headers and Property names without trouble and able to successfully connect LDAP with XML output which I have used for additional message mapping requirements.
Much appreciated for your direction!
Thanks!
Praveen Varriam
Good Blog Sana and right on time. Cheers
Hi Sana,
thank you for the blog, works like a charm.
I have one question/demand. The MS active directory usually is set to a pagesize of 1000. Let's assume that we cannot increase this limit and the data set is 15000. Is it possible to loop through the pages in the ldap adpater so I get back all the data? Can we manipulate a filter parameter for this requirement?
Regards,
Daniel
Hi Daniel,
As of now, we do not have such a feature in CPI. You can however design your integration flow with looping integration process. The filter parameter can be injected into the LDAP adapter dynamically by using it as a header or property.
Regards,
Sana
Hi Sana,
This blog is really helpful.
In continuation to your above answer, could you please explain it in more detail. You're asking us use filter paramter dynamically right, so is there a way to specify in pagination in it? If so, could you please let us know what is the syntax for it.
Regards,
Vineeth.B
Hi Sana,
We have the same situation. We need to extract records from AD however it is only returning 1,000 records.
Hope you can provide guidance how this was addressed.
Br,
Charles
Hi Daniel,
I have same query, Usually MS active directory is set to 1000 page size considering performance issues. We are able to pull only 1000 records from CPI. Did you find any way to pull full set of records using pagination or any other way?
Hi Sana,
Thank you for the blog, its really help for us to do through operation instead of script.
I am facing an issue during search a user.
when I am deploying this process it is giving me below error.
Hi Wajahat,
Looks like something is incorrect either in your Base DN or the address of the LDAP server. Could you please recheck those.
Regards,
Sana
Hi Sana,
Thank you for quick response.
My connection URL and Base DN is working good because I was using the same above mentioned search query through groovy and working well.
You can see in below screen shots when I remove the user search and only keep objectClass=user,
integration is running well.
Please advice.
Thank you,
Wajahat Imam
Hi Wajahat,
I tried a similar scenario with my AD server but unable to reproduce the issue. Could you then change the base DN to start from user and then look for your 'sAMAccountName' in the filter?
Hi Wajahat,
I found a limitation in the search with referrals in Active Directory servers. I am in the process of fixing this and it should resolve your issue.
Regards,
Sana
Hi Sana
Thank you for the blog, I am receiving the same error as Wajahat Imam when I use the scope as subtree. I do get an error free response (Empty schema) when the scope is Object. Is there a solution for this.
Thanks
LL
Hi Lavanya,
As informed by you, you have fixed the issue by adding OU to Base DN.
Hi Sana,
How can I query to get samAccount Name through SuccessFactors userId using LDAP adapter.
Hi Shruthi,
You can parameterize your userId from SuccessFactor as header or property and then use it in the query of the search operation.
Hi Sana,
I am trying to add a user in AD group using insert in LDAP connector. I am getting getting INSUFF_ACCESS_RIGHTS) error code back from AD. I have been advised by Infra team user do have sufficient access.
When I tried Modify operation, user is added to the group but LDAP call is deleting existing members of that group.
LDAP Insert Query ->
{dn=CN=GrTesting,OU=Groups,OU=APAC,DC=global,DC=company,DC=com,DC=au, DistinguishedName_Previous=null,
attributes={ObjectClass=ObjectClass: group, member=member: cn=Pushkar Patel,OU=User Accounts,OU=Test,OU=APAC,DC=global,DC=company,DC=com,DC=au}}
I am using below XML schema to create above query.
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Schema">
<xs:complexType>
<xs:sequence>
<xs:element type="xs:string" name="objectClass"/>
<xs:element type="xs:string" name="distinguishedName"/>
<xs:element name="Attributes">
<xs:complexType mixed="true">
<xs:sequence>
<xs:element type="xs:string" name="member" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Any suggestion what could be wrong here?
Thanks,
Pushkar
Hi Pushkar,
The member field allows insert of multiple attributes, modifying it in CPI removes the existing ones and adds the new one. This is currently a limitation.
A workaround solution would be to search for the existing members and then concatenate that to the new one you wish to add and then use the modify operation.
\
Hi Sana,
I have requirement to add delta query in search filter like if lastmodified is with in 30 days , i have to get those.
Please let me know any search attributes for this.
Thanks,
Saidarao
You could try writing a script which stores header with the current date and end date. Use these headers in your search field in LDAP
Hi Sana/All,
I am beginner in SAP CPI and wanted to learn about LDAP adapter. I wanted to ask, how will I get Connection parameters for this adapter, Do I need to install any software, Can you please guide here It would be a great help.
Hi Krushna,
Check this blog: https://blogs.sap.com/2017/11/05/sap-cloud-integration-cloud-connector-ldap-integration/. Should give you some tips on how to get started!
Thank you, it worked smoothly for me. So happy we have this feature now.
Hi Experts,
I'm new of CPI and using LDAP adapter to connect AD server with below parameters. However, it shows error message : "com.sap.it.rt.adapter.http.api.exception.HttpResponseException: An internal server error occured: LDAP connection has been closed."
Is there anyone could give me some hint?
Thanks in advance
Mostly likely a cloud connector issue, please check if you are missing any location id and if the internal host is configured correctly.