cancel
Showing results for 
Search instead for 
Did you mean: 

Custom Login Module - how to go on

Former Member
0 Kudos

hi everyone,

We have to develop a custom login module that uses only user id to login to the portal.

the logic:

Non-SAP Portal -


> some logic + pass userid to a Servlet------> Servlet call login module and redirect to EP ( access Iviews, pages etc)

My code in the Servlet:

LoginContext loginContext = new LoginContext("sap.com/irj*irj", new SimpleCallbackHandler("user001"));

loginContext.login();

response.sendRedirect("http://<portalurl>/irj/servlet/prt/portal/prtroot/com.sap.portal.navigation.portallauncher.default");

I thought it will login directly to the portal but it was not, although the 'login' was successful.

Can anyone tell me how to acess portal content after "loginContext.login();"?

Thanks a lot

Regards,

Liting Zhou

Accepted Solutions (1)

Accepted Solutions (1)

Sigiswald
Contributor
0 Kudos

Hi Liting,

We did something similar, although our use case is probably more complex than yours. Anyway, I tried to simplify the code (without compiling, so beware...) to obtain more or less the functionality you require: access a servlet on the portal passing two parameters: uid and redirect. The servlet is secured and because of this it'll trigger a login module stack. The login module checks if the uid matches the uid of an existing UME user. If this is the case, the user is authenticated and the servlet redirects to the redirect URL.

I can imagine you were hoping for something more simple, so <b>if someone knows of a very simple solution, please let us know</b>.

One, create a DC of type J2EE Web Module. The DC contains exactly one class: UidLoginServlet.


public class UidLoginServlet extends HttpServlet {
  public void doGet(
    HttpServletRequest request,
    HttpServletResponse response)
    throws ServletException, IOException {
      // e.g. redirect=%2Firj%2Fportal
      String redirectUrl = request.getParameter("redirect");
      response.sendRedirect(redirectUrl == null ? "/irj/portal" : redirectUrl);
  }
}

Two, he servlet should be protected, i.e. require authorization. This can be done in the <i>web.xml</i> deployment descriptor.


<web-app>
  <servlet>
    <servlet-name>UidLoginServlet</servlet-name>
    <display-name>UidLoginServlet</display-name>
    <servlet-class>com.mycompany.sap.servlet.UidLoginServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>UidLoginServlet</servlet-name>
    <url-pattern>/UidLoginServlet</url-pattern>
  </servlet-mapping>
  <security-constraint>
    <display-name>UidLoginServlet</display-name>
    <web-resource-collection>
      <web-resource-name>WebResource</web-resource-name>
      <url-pattern>/UidLoginServlet</url-pattern>
      <http-method>GET</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>myrole</role-name>
    </auth-constraint>
    <user-data-constraint>
      <transport-guarantee>NONE</transport-guarantee>
    </user-data-constraint>
  </security-constraint>
  <login-config>
    <auth-method>CLIENT-CERT</auth-method>
  </login-config>
  <security-role>
    <role-name>myrole</role-name>
  </security-role>
</web-app>

Three, configure <i>web-j2ee-engine.xml</i>. It looks something like this.


<web-j2ee-engine>
  <security-role-map>
    <role-name>myrole</role-name>
    <server-role-name>all</server-role-name>
  </security-role-map>
  <login-module-configuration>
    <login-module-stack>
      <login-module>
        <login-module-name>EvaluateTicketLoginModule</login-module-name>
        <flag>sufficient</flag>
        <options>
          <option>
            <name>ume.configuration.active</name>
            <value>false</value>
          </option>
        </options>
      </login-module>
      <login-module>
        <login-module-name>UidLoginModule</login-module-name>
        <flag>required</flag>
      </login-module>
      <login-module>
        <login-module-name>CreateTicketLoginModule</login-module-name>
        <flag>optional</flag>
        <options>
          <option>
            <name>ume.configuration.active</name>
            <value>false</value>
          </option>
          <option>
            <name>validity</name>
            <value>0</value>
          </option>
          <option>
            <name>validityMin</name>
            <value>15</value>
          </option>
        </options>
      </login-module>
    </login-module-stack>
  </login-module-configuration>
</web-j2ee-engine>

Four, create a public part for your web module DC. Choose purpose <i>assembly</i> and entity-type <i>WAR</i>.

Five, create another DC of type J2EE Enterprise Application and define the public part of the web module DC as a used DC.

Six, create a DC of type Java. It contains exactly one class: UidLoginModule.

In our case, the DC also uses 2 other standard DCs: <i>com.sap.security.api.sda</i> and <i>security_api</i>.

The login module below also dynamically assigns the role "myrole" to the user if authentication succeeds. I can imagine you don't need this "feature". But it has as additional requirement that the "myrole" should already be defined in UME.


import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;

import com.sap.engine.interfaces.security.auth.AbstractLoginModule;
import com.sap.engine.lib.security.LoginExceptionDetails;
import com.sap.engine.lib.security.http.HttpCallback;
import com.sap.engine.lib.security.http.HttpGetterCallback;
import com.sap.security.api.IRole;
import com.sap.security.api.IRoleFactory;
import com.sap.security.api.IUser;
import com.sap.security.api.IUserFactory;
import com.sap.security.api.UMException;
import com.sap.security.api.UMFactory;

public class UidLoginModule extends AbstractLoginModule {
  private static String myrole;

  private Subject subject;
  private CallbackHandler callbackHandler;
  private Map sharedState;

  private boolean loginSucceeded;
  private boolean moduleIgnored;
  private boolean nameSet;
  private String uid;

  public void initialize(
    Subject subject,
    CallbackHandler callbackHandler,
    Map sharedState,
    Map options) {
    super.initialize(subject, callbackHandler, sharedState, options);
    cleanState();
    this.subject = subject;
    this.callbackHandler = callbackHandler;
    this.sharedState = sharedState;

    if (myrole == null) {
      myrole = getRoleId("myrole");
    }
  }

  public boolean login() throws LoginException {
    HttpGetterCallback uidCallback =
      createGetterCallback(HttpCallback.REQUEST_PARAMETER, "uid");
    Callback[] callbacks = new Callback[] { uidCallback };

    try {
      callbackHandler.handle(callbacks);
    } catch (UnsupportedCallbackException e) {
      moduleIgnored = true;

      return false;
    } catch (IOException e) {
      throwUserLoginException(e, LoginExceptionDetails.IO_EXCEPTION);
    }

    String uid = (String) uidCallback.getValue();

    if (uid != null && !uid.trim().equals(""))) {
      this.uid = uid;

      try {
        refreshUserInfo(uid);
      } catch (SecurityException e) {
        cleanState();
        throwUserLoginException(e);
      }

      if (sharedState.get(AbstractLoginModule.NAME) == null) {
        sharedState.put(AbstractLoginModule.NAME, uid);
        nameSet = true;
      }

      loginSucceeded = true;

      return true;
    } else {
      moduleIgnored = true;

      return false;
    }
  }

  public boolean commit() throws LoginException {
    try {
      if (loginSucceeded) {
        com.sap.engine.lib.security.Principal principal =
          new com.sap.engine.lib.security.Principal(uid);
        subject.getPrincipals().add(principal);
        IUser user = getUser(uid);

        UMFactory.getRoleFactory().addUserToRole(
          user.getUniqueID(), myrole);

        if (nameSet) {
          sharedState.put(AbstractLoginModule.PRINCIPAL, principal);
        }

        return true;
      } else {
        IUser user =
          getUser((String) sharedState.get(AbstractLoginModule.NAME));

        if (user != null) {
          UMFactory.getRoleFactory().removeUserFromRole(uid, myrole);
        }

        cleanState();

        return false;
      }
    } catch (Exception e) {
      throwUserLoginException(e);

      return false;
    }
  }

  public boolean abort() throws LoginException {
    boolean abort = loginSucceeded;
    cleanState();

    return abort;
  }

  public boolean logout() throws LoginException {
    if (loginSucceeded) {
      subject.getPrincipals(java.security.Principal.class).clear();
    }

    cleanState();

    return true;
  }

  private void cleanState() {
    loginSucceeded = false;
    moduleIgnored = false;
    nameSet = false;
    uid = null;
  }

  private HttpGetterCallback createGetterCallback(byte type, String name) {
    HttpGetterCallback httpCallback = new HttpGetterCallback();
    httpCallback.setType(type);
    httpCallback.setName(name);

    return httpCallback;
  }

  private static String getRoleId(String uniqueName) {
    try {
      IRoleFactory rf = UMFactory.getRoleFactory();
      IRole role = rf.getRoleByUniqueName(uniqueName);

      return role.getUniqueID();
    } catch (Exception e) {
      String message =
        "Exception while getting role with unique name ""
          + uniqueName
          + "": "
          + e.getMessage();

      throw new RuntimeException(message, e);
    }
  }

  private IUser getUser(String uid) throws UMException {
    if (uid == null) {
      return null;
    }

    IUserFactory userFactory = UMFactory.getUserFactory();

    return userFactory.getUserByLogonID(uid);
  }
}

Seven, There should be a file <i>_comp\server\provider.xml</i> on your file system. If not, create it and check it into DTR. It should look something like this.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE provider-descriptor SYSTEM "library.provider.dtd">
<provider-descriptor>
  <display-name>mycompany.com/sap/j2ee/lib/logon</display-name>
  <component-name>UidLoginModuleLibrary</component-name>
  <description></description>
  <major-version>6</major-version>
  <minor-version>40</minor-version>
  <micro-version>0</micro-version>
  <provider-name>mycompany.com</provider-name>
  <references>
    <reference
      provider-name="sap.com"
      strength="weak"
      type="interface">security_api</reference>
    <reference
      provider-name="sap.com"
      strength="weak"
      type="library">com.sap.security.api.sda</reference>
  </references>
  <jars>
    <jar-name>mycompany.com~sap~java~logon~logon-assembly.jar</jar-name>
  </jars>
</provider-descriptor>

Eight, create a public part for your login module DC. Choose purpose <i>assembly</i> and entity-type <i>Java Package</i>.

Nine, create another DC of type J2EE Server Component / Library and define the public part of the login module DC as a used DC.

Ten, create a public part for your server lib DC. Choose purpose <i>compilation</i> and entity-type <i>SERVER_COMPONENTS</i>. (I doubt if this makes any sense; this is what we once did; probably you should skip this step.)

Eleven, deploy both the enterprise app and the server lib DC.

Twelve, in Visual Administrator.

  • choose the "Cluster" tab

  • navigate to "Server" / "Libraries"

  • the following library must be present: <i>mycompany.com/sap/j2ee/lib/logon</i> having Library Name: <i>mycompany.com~UidLoginModuleLibrary</i>

Thirteen, in Visual Administrator.

  • choose the "Cluster" tab

  • navigate to "Server" / "Services" / "Security Provider"

  • choose the "Properties" tab

  • select the "LoginModuleClassLoaders" property

  • set the value to <i>library:mycompany.com~UidLoginModuleLibrary</i>

  • click "Update" at the bottom and "Save Properties" at the top

Fourteen, in Visual Administrator.

  • choose the "Cluster" tab

  • navigate to "Server" / "Services" / "Security Provider"

  • choose the "Runtime" and "User Management" tabs

  • at the top, click "Switch to edit mode"

  • at the right bottom, click "Manage Security Stores"

  • at the left, select "UME User Store"

  • at the right bottom, click "Add Login Module" and immediately choose "OK"

  • enter the following values:

<i>Class Name: mycompany.com.sap.logon.UidLoginModule</i>

<i>Display Name: UidLoginModule</i>

  • click "OK"

Fifteen, in Visual Administrator.

  • choose the "Cluster" tab

  • navigate to "Server" / "Services" / "Security Provider"

  • choose the "Runtime" and "Policy Configurations" tabs

  • at the top, click "Switch to edit mode"

  • select the (already present) policy configuration "mycompany.com/sapj2eeear~uid*uid" (not sure how it's called)

  • at the top, select "ticket" as the "Authentication template"

  • at the top, select "no" as the "Authentication template"

=> the result is that there are 3 login modules available and it is possible to modify the login modules available on the stack

  • at the right bottom, click "Add New"

  • select "UidLoginModule" and click "OK"

  • select "BasicPasswordLoginModule" and click "Remove"

  • select "com.sap.security.core.server.jaas.EvaluateTicketLoginModule" and click "Modify"

  • enter the following value and click "OK":

<i>Options: ume.configuration.active=false</i>

  • select "UidLoginModule" and click "Modify"

  • enter the following values and click "OK":

<i>Position: 2</i>

<i>Flag: REQUIRED</i>

  • select "com.sap.security.core.server.jaas.CreateTicketLoginModule" and click "Modify"

  • enter the following values and click "OK":

<i>Options: ume.configuration.active=false</i>

<i>Options: validity=0</i>

<i>Options: validityMin=15</i>

  • verify the final result

Sixteen, restart the server and hope for the best...

Kind regards,

Sigiswald

Former Member
0 Kudos

hi Sigiswald

Thank you very much !

Regards,

Liting Zhou

Answers (1)

Answers (1)

Former Member
0 Kudos

Hi!

Do you know how I could dynamically assign a Portal Group based on a certain ECC Abap role the user has? I was hoping to develop a custom logon module that will connect to backend to read the users R3 roles, then have some mapping to assign a portal group....

Former Member
0 Kudos

Did you get it working? I need to do the exact same thing.

My problem is that JCO doesn't seem to be available within my module (reference to it added to the sda of course), as soon as I create an object whose implementation uses JCO (but well before JCO is actually used) I get an Exception I can't catch, like this:

2. es.indra.jaas.HeaderVariable12CharacterConversionLoginModule OPTIONAL ok exception com/sap/mw/jco/JCO$Exception

This isn't an actual exception being thrown apparently (we could catch it) but that class being missing. Could be wrong here though.