25
Wed, Dec
0 New Articles

Practical RPG: Accessing LDAP

RPG
Typography
  • Smaller Small Medium Big Bigger
  • Default Helvetica Segoe Georgia Times

Lightweight Directory Access Protocol (LDAP) servers are everywhere in enterprises today, and this article shows you how to access LDAP servers on any platform from RPG.

 

The IBM i is perhaps the most integrated and integrate-able server platform in the world today. With the exception of a native GUI (which I would argue is not required in a server platform anyway), it's hard to find a modern programming feature that isn't available on the i. Today's article is going to focus on one of those features: Lightweight Directory Access Protocol (LDAP) support. More specifically, it is going to give you a working example program of how to access any LDAP server in your organization from standard RPG.

The Focus of This Article

I won't spend a lot of time explaining LDAP itself; if you're unfamiliar with the term, you can always start at Wikipedia. A nice introductory article can be found here, and an older but even more friendly introduction was written back in 2000 and is still available online. And not surprisingly, probably the most comprehensive document is IBM's Redbook. However, I wanted to point out that LDAP has two distinct sides: the server and the client. From the server side, the IBM i is just as capable as any platform out there. IBM Tivoli Directory Server (ITDS) is easy to set up and manage, and in another article I'll try to give you a primer on getting ITDS started. It's basically a few functions in Systems i Navigator.

 

But the reality is that today many enterprises use an LDAP server other than the IBM i. That's why today's article is written from the client perspective. As it turns out, the client support on the IBM i is just as extensive as its server support. It's relatively easy to write a simple RPG program to access the data in any LDAP server in your enterprise. I did it in about 150 lines of code, and a lot of that code defined the prototypes for the LDAP programs. And so without further ado, let's take a look at the program.

The LDAP Client Program

I'll walk you through the program in detail.

 

     h option(*nodebugio:*srcstmt) dftactgrp(*no) actgrp(*new)

 

      **

      * External prototypes

      **

     d ldap_init       pr              *   extproc('ldap_init')

     d   host                          *   value options(*string)

     d   port                        10I 0 value

 

     d hLDAP           s               *

 

     d ldap_search_s   pr            10i 0 extproc('ldap_search_s')

     d   ld                            *   value

     d   base                          *   value options(*string)

     d   scope                       10I 0 value

     d   filter                        *   value options(*string)

     d   attrs                         *   value

     d   attrsonly                   10I 0 value

     d   res                           *

 

     d basedn          c                   'o=My Company'

     d filter          s             50    varying

 

     d cDescription    ds

     d                               11    inz('description')

     d                                1    inz(x'00')

 

     d attributes      ds

     d                                 *   inz(%addr(cDescription))

     d                                 *   inz(*null)

 

     d hResult         s               *

     d rc              s             10i 0

 

     d ldap_first_entry...

     d                 pr              *   extproc('ldap_first_entry')

     d   ld                            *   value

     d   result                        *   value

 

     d hEntry          s               *

 

     d ldap_get_values...

     d                 pr              *   extproc('ldap_get_values')

     d   ld                            *   value

     d   entry                         *   value

     d   attr                          *   value options(*string)

 

     d ppDescription   s               *

     d pDescription    s               *   based(ppDescription)

 

     d ldap_msgfree    pr            10i 0 extproc('ldap_msgfree')

     d   msg                           *   value

 

     d ldap_memfree    pr                  extproc('ldap_memfree')

     d   mem                           *   value

 

     d ldap_get_errno  pr            10i 0 extproc('ldap_get_errno')

     d   ld                            *   value

 

     d ldap_err2string...

     d                 pr              *   extproc('ldap_err2string')

     d   error                       10i 0 value

 

 

With the exception of a relatively standard H-spec (containing just my typical debugging options and activation group keywords to allow ILE programming), the first part of the program is made up entirely of prototypes for the LDAP APIs. You can find detailed information on the APIs in the IBM Infocenter. The LDAP APIs are a little unusual from an RPG standpoint. They tend to return pointers and even pointers to pointers. This program is very simple, so doesn't get into a lot of the more arcane features of the APIs, but it's enough to get you started.

 

You'll notice that I use the parameter names from the Infocenter for the parameter names in my prototypes. However, I find that the names in the APIs don't provide much information, so the actual RPG variables are named a bit differently. For example, pointers to normal variables start with a "p," and pointers to pointers start with "pp." Note I said "pointers to normal variables," which implies there is a different type of pointer. That different type of pointer is a handle, and the term denotes a pointer to an application opaque object (an object whose internal structure is unknown to the application). A handle starts with the letter "h." Handles are returned from APIs with the express purpose of being passed to other APIs without being touched or even referenced by the application. So to summarize, a variable starting with "p" points to an RPG variable, while one starting with "h" is intended to be passed only to another API.

 

      **

      * Internal prototypes

      **

 

     dGetDescription   pr             3u 0

 

     d stage           s              3u 0

 

     dFormatError      pr            80

     d errorstage                     3u 0

 

 These internal prototypes are provided for my friends on back releases. Those of you on a current release can ignore these statements. Lucky you!

 

      **

      * Main prototype

      **

     d LDAPTEST        pr                  extpgm('LDAPTEST')

     d   iUser                       10

     d   iDescription                80

 

     d LDAPTEST        pi

     d   iUser                       10

     d   iDescription                80

 

This is the primary definition of the program. What does the program do? It provides the description associated with a user name. The sharp-eyed will note that my user name is only 10 characters; that's because my LDAP server is an IBM i, so my user ID is actually my user profile name. But certainly if you were accessing a different server you could provide a longer user ID field. For that matter, your description field could be larger, too, or you could add more attributes. That's the beauty of an example program; you can do whatever you need with it.

 

      /free

 

       stage = GetDescription();

       if stage > 0;

         iDescription = FormatError(stage);

       endif;

 

       if ppDescription <> *null;

         ldap_memfree(ppDescription);

       endif;

 

       if hEntry <> *null;

         ldap_memfree(hEntry);

       endif;

 

       if hResult <> *null;

         ldap_msgfree(hResult);

       endif;

 

       *inlr = *on;

       return;

 

      /end-free

 

I noted that this is a very simple program. That doesn't mean it's trivial; this program provides a simple but useful function: it retrieves a single attribute (in this case, the description) from a specific entry. That's only a tiny subset of what LDAP can do, but this is meant to be your first working program.

 

The mainline has very little LDAP-specific code other than cleanup. But cleanup is important; the LDAP APIs tend to allocate memory, and if your application doesn't free that memory, you can end up with the infamous memory leak—a program that slowly eats more and more memory until serious system errors start to occur. You'll see that I call the getDescription procedure, and then, regardless of the results, I free up all allocated pointers (I assume a pointer is allocated if it is not null).

 

I'd like to describe for a moment the error processing. The LDAP APIs have their own error processing, which I'll show at the end of the example. But the way this program works is that it executes its logic in stages, and the getDescription functions returns a non-zero stage number if an error occurs. If an error does occur, the mainline calls the formatError procedure to return a human-readable error message instead of the description.

 

     pGetDescription   b

     d                 pi             3u 0

      /free

       hResult = *null;

       hEntry = *null;

       ppDescription = *null;

 

This is the meat of the program. I'm going to review each step of the process. The first thing I do is set all the pointers to null, telling the mainline not to deallocate them. Then I start the actual communication with the LDAP server.

 

       hLDAP = ldap_init('MYLDAPHOST': 389);

       if hLDAP = *NULL;

         return 1;

       endif;

 

The first step, ldap_init, is standard. You provide the host name and port. The traditional port is 389, and then you supply a resolvable name for your server. You can use *NULL as the host to cause it to use the local host. Why, then, since I'm using the IBM i as my server and my client code is by definition running on the IBM i, don't I use *NULL? Well, since this is an example, I wanted to show you some of the flexibility. By changing the host, I can easily use a different IBM i as my LDAP server—or another machine entirely, including a Linux box or even a Microsoft Active Directory server. All from my little RPG program. Pretty slick, eh?

 

       filter = '(cn=' + %trim(iUser) + ')';

       rc = ldap_search_s(

              hLDAP: basedn: 2: filter: %addr(attributes): 0: hResult);

       if rc <> 0;

         return 2;

       endif;

 

Next, the program calls ldap_search_s. The short explanation is that this is a routine that retrieves as many entries as match the filter criteria. My filter is almost criminally simple; it looks for entries that have a common name attribute (cn=) that exactly matches my user ID. It just so happens that this is the way that the IBM i populates the directory. Without going into too much detail, let me just say that the criteria can be very extensive, with all manner of Boolean logic and fuzzy comparisons. This page provides a good introduction to filter syntax. The API also requires an array of pointers to strings to tell the system which attributes to retrieve. My list is again very simple: it is a list with one entry, the attribute named "description."

 

Other ldap_search APIs exist. The one I selected is synchronous, meaning it waits for the operation to complete but has no timeout, which means it waits forever. Others function differently. The search routine you select depends on your application requirements. Also, since the program never performed a bind operation, it is accessing the directory anonymously. A good overview of LDAP authentication can be found here.

 

       hEntry = ldap_first_entry(hLDAP: hResult);

       if hEntry = *null;

         return 3;

       endif;

 

The next steps are very simple, in part because my requirement is simple. The ldap_first_entry call returns a handle to the first matching entry. If the handle is null, I return an error.

 

       ppDescription = ldap_get_values(hLDAP: hEntry: cDescription);

       if ppDescription = *null;

         return 4;

       endif;

 

The final step is to retrieve the description. I call the ldap_get_values API to return a list of all values named "description." You would correctly surmise that an entry could have multiple values for a single attribute. In this case, though, I assume that there is exactly one value; I only bother to get the first value. If there is no value, return an error.

 

       iDescription = %str(pDescription:80);

       return 0;

      /end-free

     p                 e

 

As I noted, if I successfully retrieve at least one value, I simply set the description parameter to the first retrieved value and return a value of zero, signifying successful completion.

 

     pFormatError      b

     d                 pi            80

     d errorstage                     3u 0

     d msg             s             80    varying

      /free

       msg = '**ERR: Stage ' + %char(errorstage);

       if rc = 0;

         rc = ldap_get_errno(hLDAP);

       endif;

       if rc <> 0;

         msg += (': ' + %str(ldap_err2string(rc)));

       endif;

       return msg;

      /end-free

     p                 e                     

 

 All that's left is the error formatting. Most routines simply signal an error; the application program then calls lda_get_errno to retrieve the error code. Interestingly, the ldap_search routines don't work that way; they return their error code directly. So, the common error routine I've written checks to see if rc already has an error and, if not, calls ldap_get_errno. A subsequent call to ldap_err2string converts the error code into a human-readable string. Append that to the stage number and you have your completed error message.

 

That's all there is to it. As I noted, this is a very simple example. But it should give you enough to get started with LDAP processing. I particularly like the fact that you can query an Active Directory server from an RPG program. It just proves again the flexibility of my i whenever I can directly access Microsoft objects. Have fun!

as/400, os/400, iseries, system i, i5/os, ibm i, power systems, 6.1, 7.1, V7, V6R1

BLOG COMMENTS POWERED BY DISQUS

LATEST COMMENTS

Support MC Press Online

$

Book Reviews

Resource Center

  • SB Profound WC 5536 Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application. You can find Part 1 here. In Part 2 of our free Node.js Webinar Series, Brian May teaches you the different tooling options available for writing code, debugging, and using Git for version control. Brian will briefly discuss the different tools available, and demonstrate his preferred setup for Node development on IBM i or any platform. Attend this webinar to learn:

  • SB Profound WP 5539More than ever, there is a demand for IT to deliver innovation. Your IBM i has been an essential part of your business operations for years. However, your organization may struggle to maintain the current system and implement new projects. The thousands of customers we've worked with and surveyed state that expectations regarding the digital footprint and vision of the company are not aligned with the current IT environment.

  • SB HelpSystems ROBOT Generic IBM announced the E1080 servers using the latest Power10 processor in September 2021. The most powerful processor from IBM to date, Power10 is designed to handle the demands of doing business in today’s high-tech atmosphere, including running cloud applications, supporting big data, and managing AI workloads. But what does Power10 mean for your data center? In this recorded webinar, IBMers Dan Sundt and Dylan Boday join IBM Power Champion Tom Huntington for a discussion on why Power10 technology is the right strategic investment if you run IBM i, AIX, or Linux. In this action-packed hour, Tom will share trends from the IBM i and AIX user communities while Dan and Dylan dive into the tech specs for key hardware, including:

  • Magic MarkTRY the one package that solves all your document design and printing challenges on all your platforms. Produce bar code labels, electronic forms, ad hoc reports, and RFID tags – without programming! MarkMagic is the only document design and print solution that combines report writing, WYSIWYG label and forms design, and conditional printing in one integrated product. Make sure your data survives when catastrophe hits. Request your trial now!  Request Now.

  • SB HelpSystems ROBOT GenericForms of ransomware has been around for over 30 years, and with more and more organizations suffering attacks each year, it continues to endure. What has made ransomware such a durable threat and what is the best way to combat it? In order to prevent ransomware, organizations must first understand how it works.

  • SB HelpSystems ROBOT GenericIT security is a top priority for businesses around the world, but most IBM i pros don’t know where to begin—and most cybersecurity experts don’t know IBM i. In this session, Robin Tatam explores the business impact of lax IBM i security, the top vulnerabilities putting IBM i at risk, and the steps you can take to protect your organization. If you’re looking to avoid unexpected downtime or corrupted data, you don’t want to miss this session.

  • SB HelpSystems ROBOT GenericCan you trust all of your users all of the time? A typical end user receives 16 malicious emails each month, but only 17 percent of these phishing campaigns are reported to IT. Once an attack is underway, most organizations won’t discover the breach until six months later. A staggering amount of damage can occur in that time. Despite these risks, 93 percent of organizations are leaving their IBM i systems vulnerable to cybercrime. In this on-demand webinar, IBM i security experts Robin Tatam and Sandi Moore will reveal:

  • FORTRA Disaster protection is vital to every business. Yet, it often consists of patched together procedures that are prone to error. From automatic backups to data encryption to media management, Robot automates the routine (yet often complex) tasks of iSeries backup and recovery, saving you time and money and making the process safer and more reliable. Automate your backups with the Robot Backup and Recovery Solution. Key features include:

  • FORTRAManaging messages on your IBM i can be more than a full-time job if you have to do it manually. Messages need a response and resources must be monitored—often over multiple systems and across platforms. How can you be sure you won’t miss important system events? Automate your message center with the Robot Message Management Solution. Key features include:

  • FORTRAThe thought of printing, distributing, and storing iSeries reports manually may reduce you to tears. Paper and labor costs associated with report generation can spiral out of control. Mountains of paper threaten to swamp your files. Robot automates report bursting, distribution, bundling, and archiving, and offers secure, selective online report viewing. Manage your reports with the Robot Report Management Solution. Key features include:

  • FORTRAFor over 30 years, Robot has been a leader in systems management for IBM i. With batch job creation and scheduling at its core, the Robot Job Scheduling Solution reduces the opportunity for human error and helps you maintain service levels, automating even the biggest, most complex runbooks. Manage your job schedule with the Robot Job Scheduling Solution. Key features include:

  • LANSA Business users want new applications now. Market and regulatory pressures require faster application updates and delivery into production. Your IBM i developers may be approaching retirement, and you see no sure way to fill their positions with experienced developers. In addition, you may be caught between maintaining your existing applications and the uncertainty of moving to something new.

  • LANSAWhen it comes to creating your business applications, there are hundreds of coding platforms and programming languages to choose from. These options range from very complex traditional programming languages to Low-Code platforms where sometimes no traditional coding experience is needed. Download our whitepaper, The Power of Writing Code in a Low-Code Solution, and:

  • LANSASupply Chain is becoming increasingly complex and unpredictable. From raw materials for manufacturing to food supply chains, the journey from source to production to delivery to consumers is marred with inefficiencies, manual processes, shortages, recalls, counterfeits, and scandals. In this webinar, we discuss how:

  • The MC Resource Centers bring you the widest selection of white papers, trial software, and on-demand webcasts for you to choose from. >> Review the list of White Papers, Trial Software or On-Demand Webcast at the MC Press Resource Center. >> Add the items to yru Cart and complet he checkout process and submit

  • Profound Logic Have you been wondering about Node.js? Our free Node.js Webinar Series takes you from total beginner to creating a fully-functional IBM i Node.js business application.

  • SB Profound WC 5536Join us for this hour-long webcast that will explore:

  • Fortra IT managers hoping to find new IBM i talent are discovering that the pool of experienced RPG programmers and operators or administrators with intimate knowledge of the operating system and the applications that run on it is small. This begs the question: How will you manage the platform that supports such a big part of your business? This guide offers strategies and software suggestions to help you plan IT staffing and resources and smooth the transition after your AS/400 talent retires. Read on to learn: