Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
hristo_borisov
Explorer

Introduction

As of version 8.0, SAP Identity Management has a new redesigned java-based Lotus Notes Connector. It leverages completely the Lotus Domino’s server java API to greatly simplify the initial setup and to lower the cost of its consecutive usage by calling methods remotely using CORBA. This way it is no longer necessary to have the Lotus Notes Client installed locally on the Identity Management system nor to rely on Visual Basic or C components.

Since the Domino API’s resetUserPassword method is only supported on the server and cannot be invoked remotely, the Identity Management’s connector has to call a dedicated Domino Agent for this task.

In this blog post I am going to provide detailed instructions how to implement such Agent and how to use it with SAP Identity Management.

Lotus Domino Agents

So what exactly is a Lotus Domino agent? As we can read in the IBM’s documentation: “Agents are stand-alone programs that perform a specific task in one or more databases. Agents are the most flexible type of automation…”. These stand-alone programs run on the Domino server and can be invoked either directly, based on event, or scheduled to run periodically. In our case, we are going to call Agent’s methods directly from the Identity Management Java connector.

Implementing Password Reset Agent

To implement a Lotus Domino Agent, we will need the IBM Lotus Domino Designer which can be downloaded from here: http://www-03.ibm.com/software/products/en/ibmdominodesigner

After you have the Designer up and running, we can start with the implementation.

Creating the Application

  1. Create new application by clicking on File -> New -> Application.
  2. In the dialog window, enter the details about the agent.
    1. For server, select the server on which the agent will be running. This is where your ID Vault is.
    2. For title, use “IDM Password Reset”. You can see that a file name is automatically generated. In my case, that is “IDMPassw.nsf”. Leave it as it is.
  3. Click OK to create the application.

    The new application is created and automatically opened. You can see in the tree on the left hand side that an application can have many elements: Forms, views XPages, etc. For our use case we are only interested in the Code node where the agents are defined.
  4. Expand it and right-click on the Agents node.
  5. Select “New Agent…”.
  6. In the new dialog window, enter the details of the agent.
    1. For name, enter “IDMPasswordResetter”.
    2. For alias “IDM Password Resetter”.
    3. If you want, you can enter a comment in the next field.
    4. Very important is to select the proper type of the agent. In our case, this is – “Java” and not “Imported Java”. Make sure that the agent will be created in the application we just created. You can check that in the last drop down.
  7. Click OK to create the Agent.

Putting the Password Reset Logic

The new agent is created and automatically opened. You can see that in the Agent Contents tree there is a source folder with default package and Java class generated. Open the JavaAgent.java class.

You can see that inside, we have a NotesMain() method which is the entry point of the agent.

Now we have a placeholder for the resetUserPassword method which is part of the Session class. You can find its documentation here: http://www-01.ibm.com/support/knowledgecenter/SSVRGU_8.5.3/com.ibm.designer.domino.main.doc/H_RESETU.... From the documentation, we can read that the method “…is only supported on the server.” And “This method is only supported in agents.” Also it takes three string arguments: servername, username and password. The server name is the “Canonical name of the server or servers to execute the agent”. The username is the one of the user we want to change the password for. The password is the new password for this user.

As we saw earlier, the NotesMain() method which is the entry point of the agent does not have any parameters. So how do we send the servername, username and password from Identity Management? The Lotus Notes connector uses the runOnServer() method of the Domino API’s Agent class to run the password reset agent. This method comes in two flavors: the first one without any parameters, which just invokes the agent’s code (NotesMain()). The second one has one string argument which is the note id of a Notes document.

Notes Document or Note is a central part of the Lotus Notes architecture. It is an object data store which “is a compound structure of mixed data types arranged in fields that can be arbitrarily modified and extended. A note may contain text, rich text, binary blobs (attachments, ActiveX or Java applets, for instance), encryption keys, doclinks, and so on. Each Notes database contains a collection of notes, and includes meta-organizing structures for display, security, retrieval and access rights to the notes.”

In our case, we create a note document inside the Agent’s database and use this document to pass the parameters to the password reset method. So prior to invoke the agents code, the Identity Management connector creates a new Note and puts three key-value pairs: server, username and password with their respective values. Once the document is created, the connector gets its Note Id (or unique identifier) and passes it to the runOnServer() method.

On the agent’s side we have to open that document and extract the parameters from it. This is done in the following way:

First we need to acquire the current agent’s instance with the following code:


Agent thisAgent = agentContext.getCurrentAgent();



You can see that we use the agentContext which is already available in the generated agent code. Next we have to acquire the note id of the document holding the parameters:


String paramid =  thisAgent.getParameterDocID();



Once we have the document id, we need to look up the agent’s database and from it to extract the document using its ID:


Database db = agentContext.getCurrentDatabase();
Document doc = db.getDocumentByID(paramid);



Now we have the document with our three parameters, but how to get their values? For this purpose we will use the getItemValue() method of the Document class. It has one parameter - the name of the item or in our case the parameter and returns a java.util.Vector of values. So to get the values of the three parameters, we can use these statements:


           Vector serverVector = doc.getItemValue("server");
            String server = null;
            if (serverVector != null && serverVector.size() > 0) {
                 server = (String) serverVector.get(0);
            }
            Vector usernameVector = doc.getItemValue("username");
            String username = null;
            if (usernameVector != null && usernameVector.size() > 0) {
                 username = (String) usernameVector.get(0);
            }
            Vector passwordVector = doc.getItemValue("password");
            String password = null;
            if (passwordVector != null && passwordVector.size() > 0) {
                 password = (String) passwordVector.get(0);
            }



Now we have all we need to call the method that will actually reset the user’s password:


session.resetUserPassword(server, username, password);


It is important that the resetUserPassword method as well as most methods from the Notes API can throw a NotesException. This is a checked exception and you have to surround its invocation with try-catch block. Also it is peculiar that the NotesException class is not standard exception – if you try to get the exception’s message using getMessage() method or the stack trace using getStackTrace(), you will most likely get something useless. Instead, the NotesException class has three public fields: id, text and internal. The first two are the error’s code and text description and the third one is the stack trace of the exception. So if you want to print useful error message to the log, use the id and the text fields.

To actually get a logging object with which to log the agent’s actions in a text file for example, you can use this snippet:


     Log log = session.createLog(thisAgent.getName());
     log.openAgentLog();
     log.openFileLog("path-to-log-file.log");


Then you can use the logAction() method to log events:


log.logAction("Exception: " + e.id + “ – “ + e.text);


To return value back to the Identity Management connector, we will use the same Notes document with which the input parameters were passed. We will use the replaceItemValue() method of the Document class to add new key-value pair holding the result:


  doc.replaceItemValue("result", Boolean.valueOf(isReset));
  doc.save(true,true);


It is important to call the save() method to actually commit the changes.

Note: Unfortunately there is a weird quirk in the resetUserPassword() java method. It always returns false even if the password is reset successfully. For this reason the result handling is a little odd we have to check for NotesExceptions during password reset: if there are such, we return false otherwise we return true.

Security

Once the agent is implemented, we have to set the appropriate access rights so that it would be able to reset passwords. First double-click on the agent to open its content. You should see the Properties view. It has three tabs: Basics, Security and Document Selection. In the Basics tab, you have to set the Runtime area settings as follows:

  • Trigger: On event
  • Action menu selection
  • Target: None

The rest should be with the default values. In the Security tab:

  • Uncheck the “Run as Web user” checkbox
  • Run on behalf of: should be empty
  • Runtime security level: 2. Allow restricted operations

Next step is to make the user used to sign the agent to be a “Designated Password Re-setter”. First you have to check which username is used to sign the agent. In the Domino Designer, go to: “File -> Security -> User Security…”. In the “Who You Are” area you can see the Name value. In my case this is “Administrator/sap”.

  1. Now go to the IBM Domino Administrator and select the Configuration tab. From the tree on the left, select Security -> ID Vaults node and select your ID Vault from the list. Then from the menu on the right-hand side, select Tools -> ID Vaults -> Manage… -> Add or remove password reset authorities.
  2. From the Password reset authority by organization list, select the organization or organizational unit of users whose passwords will be reset.
  3. From the Available users, groups and servers list, select the name of the user that has signed (or will sign) the application agent.
  4. Click Add to give the selected agent signer password reset authority for the organization or organizational unit highlighted.
  5. Keep the agent signer name highlighted and select Self-service password reset authority.
  6. From the Available users, groups and servers list, select the name of a server or group of servers on which you will deploy the application.
  7. Click Add to give the selected server or server group password reset authority for the organization or organizational unit highlighted.
  8. Repeat the steps above if necessary

For more details have a look at the IBM Domino Documentation: http://www-01.ibm.com/support/knowledgecenter/SSKTMJ_9.0.1/admin/conf_assigningpasswordresetauthorit...

Wrap it up

Source code:


public class JavaAgent extends AgentBase {
  public void NotesMain() {
       Log log = null;
       Document parametersDocument = null;
       try {
            Session session = getSession();
            AgentContext agentContext = session.getAgentContext();
            Agent currentAgent = agentContext.getCurrentAgent();
            Database currentDatabase = agentContext.getCurrentDatabase();
            log = session.createLog(currentAgent.getName());
            log.openAgentLog();
            log.openFileLog("C:\\D\\agentlogBackend2.txt");
            String parametersDocumentId = currentAgent.getParameterDocID();
            parametersDocument = currentDatabase.getDocumentByID(parametersDocumentId);
            Vector serverVector = parametersDocument.getItemValue("server");
            String server = null;
            if (serverVector != null && serverVector.size() > 0) {
                 server = (String) serverVector.get(0);
            }
            Vector usernameVector = parametersDocument.getItemValue("username");
            String username = null;
            if (usernameVector != null && usernameVector.size() > 0) {
                 username = (String) usernameVector.get(0);
            }
            Vector passwordVector = parametersDocument.getItemValue("password");
            String password = null;
            if (passwordVector != null && passwordVector.size() > 0) {
                 password = (String) passwordVector.get(0);
            }
            log.logAction("Reseting password...");
            session.resetUserPassword(server, username, password);
            parametersDocument.replaceItemValue("result", "true");
            parametersDocument.save(true, true);
            log.logAction("Return value: true");
       } catch (NotesException e) {
            try {
                 log.logError(e.id, e.text);
                 parametersDocument.replaceItemValue("result", "false");
                 parametersDocument.save(true, true);
            } catch (NotesException e1) {
                 e1.printStackTrace();
            }
       }
  }
}