MicroStrategy ONE

Code Explanation for Scenario: Mapping Credentials using an External Repository

In this scenario, you create a custom ESM that attempts to map a user-supplied login and password to a corresponding set of valid MicroStrategy login credentials stored in an external user repository. To accomplish this customization, you write code to override the processMSTRLoginForm method, which uses the form fields on the default login page as it performs this mapping.

Custom ExternalSecurity java class  (calledUserMapping.javain the corresponding scenario)

A generic explanation of how to create the code required to perform this customization is provided below. The code is explained in sections, with the explanation preceding each section of code.

  • Explanation: Specify the package in which this class will reside and import the necessary classes, including AbstractExternalSecurity (which contains default implementations for all of the methods). Declare that this custom External Security class (compiled from the sample java file called UserMapping.java in the corresponding scenario) extends AbstractExternalSecurity.:

    Copy
    package com.microstrategy.sdk.samples.externalsecurity;
    import java.sql.*;
    import com.microstrategy.web.app.AbstractExternalSecurity;
    import com.microstrategy.web.app.LoginForm;
    import com.microstrategy.web.beans.RequestKeys;
    import com.microstrategy.web.objects.WebObjectsFactory;
    import com.microstrategy.web.objects.WebIServerSession;
    import com.microstrategy.web.platform.ContainerServices;
    public class UserMapping extends AbstractExternalSecurity{
  • Explanation: Override the processMSTRLogingForm method to add custom code to map user credentials. Because the default MicroStrategy Web login page is being used to collect the user credentials, the first step is to get the user-supplied login and password from the login form and pass it to the helper method, validateLogin. The implementation of the validateLogin method, which maps the user-supplied credentials to the proper MicroStrategy Intelligence Server login credentials, is explained in a later step, but for now you can think of it as a generic method that maps one set of authentication information to another. The return value is an array of two strings where the first string is the mapped login (that is, the corresponding MicroStrategy login) and the second string is the mapped password (that is, the corresponding MicroStrategy password). If an error is encountered or no mapping is found, null is returned.:

    Copy
    public boolean processMSTRLoginForm(RequestKeys reqKeys, ContainerServices cntSvcs, LoginForm loginForm, int reason)
      {
      //Get the values from the form and use them to query external authentication database
      String lEnteredUID = loginForm.getLoginName();
      String lEnteredPWD = loginForm.getPassword();
      
      String lMSTRLoginInfo[] = validateLogin(lEnteredUID, lEnteredPWD);
       
  • Explanation: If the validateLogin method returns a proper set of credentials, use them to create a MicroStrategy Intelligence Server session. First, obtain the server and project information from the RequestKeys parameter so that it can be used when building the session. Next, pass the mapped login and password information in to the session object. Finally, set the session on the LoginForm, and mark the LoginForm status as being populated.:

    Copy
      WebIServerSession lISS = null;
      
      if(lMSTRLoginInfo != null)
        {
        String lServer = reqKeys.getValue("Server");
        String lProject = reqKeys.getValue("Project");
        lISS = WebObjectsFactory.getInstance().getIServerSession();
        lISS.setServerName(lServer);
        lISS.setProjectName(lProject);\
        lISS.setServerPort(0);
        lISS.setLogin(lMSTRLoginInfo[0]);
        lISS.setPassword(lMSTRLoginInfo[1]);
        loginForm.setWebIServerSession(lISS);
        loginForm.setFormStatus(true);
       }
  • Explanation: If the validateLogin method does not return a proper set of credentials, mark the LoginForm status as not being populated and set an error message. Normally, this error message is displayed on the top of a new login page that gives the user a chance to log in again.:

    Copy
      else
        {
        loginForm.setFormStatus(false);
        loginForm.setFormErrorMessage("Invalid login or password.  Please try again.");
        }
  • Explanation: Since this method returns a Boolean to signal its success or failure in populating the LoginForm, use the status as the return value.:

    Copy
    return loginForm.getFormStatus();
      }

A fairly simple-minded description of the private validateLogin method is provided below. While this method is kept simple in this example, a real implementation would be more complex and would certainly spend more effort on error handling. For the purposes of this sample, however, the validateLogin method simply looks up the user-entered credentials in a Microsoft Access database and returns the corresponding login and password from that same table if a match is found. Otherwise, it returns null and the caller is responsible for dealing with the consequences.

  • Explanation: First, the basic information that will be used to access the database is set up. The column names correspond to the ones used in the sample Access database supplied with this example code. To simplify matters, the Java JDBC-to-ODBC bridge is used. lMSTRLoginInfo is initialized to null so that if no match is found in the database or some other problem is encountered, this value can be returned directly. In a production implementation, errors would be handled in more detail than this simple approach allows. :

    In this example, the ODBC DSN is named "ESMSample". If you want to work through this example on your system, you need to set up the provided Access database as an ODBC connection with the DSN “ESMSample”.

    Copy
      private String[] validateLogin(String iEnteredUID, String iEnteredPWD)
        {
        String lSQLString = "SELECT MSTRUSERID, MSTRUSERPWD FROM LOGINS WHERE UID='" + iEnteredUID + "' AND PWD='" + iEnteredPWD + "'";
        String[] lMSTRLoginInfo = null;
        //Define the Data Source for the driver
        String lDBSourceURL = "jdbc:odbc:ESMSample";
      
        //Connect to Database
        Connection lConnection;
  • Explanation: Now, the database connection to the sample Access database is set up. The driver is loaded first and then the connection and a Statement are made. The statement allows the execution of a hard-coded SQL string query against the database. If the query is successful, it returns a Java ResultSet object.:

    Copy
        try
          {
          //Set class name to act as JDBC driver for Access
          Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
          lConnection = DriverManager.getConnection(lDBSourceURL);
          Statement lStatement = lConnection.createStatement();
          ResultSet lRS = lStatement.executeQuery(lSQLString);
  • Explanation: In this example, the ResultSet contains only a single matching row. If a match is found, a new string array is allocated and the matching login and password are read into it. For the sake of this simple example, null password entries are mapped to empty strings so that the MicroStrategy login process will operate properly.:

    Copy
          // There should be only 1 row that matches
          if(lRS.next())
            {
            lMSTRLoginInfo = new String[2];
            lMSTRLoginInfo[0] = lRS.getString(1);
            lMSTRLoginInfo[1] = lRS.getString(2);
            if(lMSTRLoginInfo[1] == null)
              {
              lMSTRLoginInfo[1] = "";
              }
                     }
  • Explanation: Because this simple sample is intended primarily to illustrate how to perform user mapping, very little attention has been paid to error handling. In real production code, you would want to report an error to the user along with a useful error message. You would probably want to do this by throwing an exception here to be caught above.:

    Copy
          lRS.close();
          lStatement.close();
          lConnection.close();
          }
        //Explanation: Finally we do a bit of cleanup on our JDBC connection.
          catch (Exception e)
            {
            System.out.println("Error executing login lookup against the DB: " + e.toString());
            }
  • Explanation: Finally, the login and password String array defined above are returned. In this simple example, either a valid MicroStrategy Intelligence Server login and password are returned or a null is returned to signal an error.:

    Copy
        return(lMSTRLoginInfo);
      }
    }