Azim Azim - 12 days ago 5
Java Question

JAAS configuration for WebLogic

I have my Web Application up and running in tomcat. Now, I have to run my application(war file) inside WebLogic 12c.

I could not find sufficient information for:

1) Is weblogic.xml mandatory along with web.xml inside war file? Tutorials are mentioning about setting the context and some more configurations in weblogic.xml but no where I could figure out that its mandatory.

2) I need to configure JAAS realm inside WebLogic. I tried configuring the realm but dont know something is screwed with the configurations. Can someone point to me proper tutorial or provide the steps required for JAAS setup.

I added -Djava.security.auth.login.config=%DOMAIN_HOME%\jaas.config inside startWebLogic.cmd file.

Below is my login module code:

public class AuthLoginModule implements LoginModule {
private static Logger logger = Logger.getLogger(AuthLoginModule.class);
// initial state
private Subject subject;
private CallbackHandler callbackHandler;
private Map<String, ?> sharedState;
private Map<String, ?> options;
// the authentication status
private boolean succeeded = false;
private boolean commitSucceeded = false;

// username and password
private String username;
private String password;
Map<String,String> userData = new HashMap<String,String>();

private AuthPrincipal userPrincipal;

public AuthLoginModule() throws WebAuthServiceException {
super();
}

public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
this.subject = subject;
this.callbackHandler = callbackHandler;
this.setSharedState(sharedState);
this.setOptions(options);
String appName = options.get(WebAuthConstants.APP_UNIQUE_NAME).toString();
logger.info("AppName in AuthLoginModule: " + appName);
}

public boolean login() throws LoginException {
if (callbackHandler == null)
throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");

Callback[] callbacks = new Callback[2];
callbacks[0] = new NameCallback("user name: ");
callbacks[1] = new PasswordCallback("password: ", false);
try {
callbackHandler.handle(callbacks);
username = ((NameCallback) callbacks[0]).getName();
char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
if (tmpPassword == null) {
// treat a NULL password as an empty password
tmpPassword = new char[0];
}
password = new String(tmpPassword);
if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
throw new LoginException("User name or password is empty");
}

} catch (java.io.IOException ioe) {
throw new LoginException(ioe.toString());
} catch (UnsupportedCallbackException uce) {
throw new LoginException("Error: " + uce.getCallback().toString() + " not available to garner authentication information " + "from the user");
}
String validateUserCredData = validateUserCred();
if (validateUserCredData!=null) {
if(JsonUtil.jsonFromString(validateUserCredData).get("statusCode").getAsInt()== HttpStatus.SC_UNAUTHORIZED) {
userData.put(DataConstants._USER_PWD_STATUS, DataConstants._RESET_USER_PWD);
}
succeeded = true;
} else {
succeeded = false;
}
return succeeded;
}


private String validateUserCred() {
try {
logger.info("Started validating user credentials for: " + username);
// If there is no error then user allowed to access all
UserClientService UserClientService = ClientServiceFactory.getInstance().getUserService();
return UserClientService.validateUserCredentials(username, password);
} catch (Throwable e) {
logger.error("Exception while authentication user against Service API, Error Code: ", e);
}
return null;
}

public boolean commit() throws LoginException {
if (succeeded == false) {
return false;
} else {
// add a Principal (authenticated identity) to the Subject
// assume the user we authenticated is the SamplePrincipal
userPrincipal = new AuthPrincipal(username, password, userData);
if (!subject.getPrincipals().contains(userPrincipal))
subject.getPrincipals().add(userPrincipal);
logger.info("Login Module successfully added user principal");
// in any case, clean out state
username = null;
password = null;
commitSucceeded = true;
return true;
}
}

public boolean abort() throws LoginException {
if (succeeded == false) {
return false;
} else if (succeeded == true && commitSucceeded == false) {
// login succeeded but overall authentication failed
succeeded = false;
username = null;
password = null;
userPrincipal = null;
} else {
// overall authentication succeeded and commit succeeded,
// but someone else's commit failed
logout();
}
return true;
}

public boolean logout() throws LoginException {
subject.getPrincipals().remove(userPrincipal);
succeeded = false;
succeeded = commitSucceeded;
username = null;
password = null;
userPrincipal = null;
logger.info("Login Module successfully removed user principal after successful logout");
return true;
}

public Map<String, ?> getSharedState() {
return sharedState;
}

public void setSharedState(Map<String, ?> sharedState) {
this.sharedState = sharedState;
}

public Map<String, ?> getOptions() {
return options;
}

public void setOptions(Map<String, ?> options) {
this.options = options;
}
}


In some of the tutorials, I could see, LoginModule written specific to WebLogic, but I feel LoginModule should not change for any server as it follows J2EE.

Any help would be appreciated.

Answer

Finally,I made Form Based JAAS configuration to work as per my needs. I followed the oracle docs link provided by KC Wong in the comments section and some blogs as well.

Below are the steps that I followed.

1) First I need to create a Custom Authentication Provider. I created a Java Project for that which includes my LoginModule, AuthenticationProviderImpl(which implements AuthenticationProviderV2) and MBean XML. This MBean XML contains the information about the MBeanType and MBeanAttribute. I posted some important files below for more information.

WLAuthenticationProviderImpl .java

package com.abc.wls.security.providers.authentication;

import java.util.HashMap;

import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;

import weblogic.management.security.ProviderMBean;
import weblogic.security.provider.PrincipalValidatorImpl;
import weblogic.security.spi.AuthenticationProviderV2;
import weblogic.security.spi.IdentityAsserterV2;
import weblogic.security.spi.PrincipalValidator;
import weblogic.security.spi.SecurityServices;

public class WLAuthenticationProviderImpl implements AuthenticationProviderV2 {
    private String description = "MyOwn WLAuthentication Provider";
    // private SimpleSampleAuthenticatorDatabase database;
    private LoginModuleControlFlag controlFlag;

    public void initialize(ProviderMBean mbean, SecurityServices services) {
        System.out.println("WLAuthenticationProviderImpl.initialize");
        // WLAuthenticationProviderMBean mbean =
        // (WLAuthenticationProviderMBean) mbean;

        // SimpleSampleAuthenticatorMBean myMBean =
        // (SimpleSampleAuthenticatorMBean) mbean;
        // description = myMBean.getDescription() + "\n" + myMBean.getVersion();
        // database = new SimpleSampleAuthenticatorDatabase(myMBean);
        // String flag = myMBean.getControlFlag();
        /*
         * if (flag.equalsIgnoreCase("REQUIRED")) { controlFlag =
         * LoginModuleControlFlag.REQUIRED; } else if
         * (flag.equalsIgnoreCase("OPTIONAL")) { controlFlag =
         * LoginModuleControlFlag.OPTIONAL; } else if
         * (flag.equalsIgnoreCase("REQUISITE")) { controlFlag =
         * LoginModuleControlFlag.REQUISITE; } else if
         * (flag.equalsIgnoreCase("SUFFICIENT")) { controlFlag =
         * LoginModuleControlFlag.SUFFICIENT; } else { throw new
         * IllegalArgumentException("invalid flag value" + flag); }
         */
    }

    public String getDescription() {
        System.out.println("WLAuthenticationProviderImpl.getDescription");
        return description;
    }

    public void shutdown() {
        System.out.println("WLSecurityProviderImpl.shutdown");
    }

    private AppConfigurationEntry getConfiguration(HashMap options) {
        System.out.println("WLAuthenticationProviderImpl.getConfiguration");
        if (options == null)
            options = new HashMap<>();
        options.put("app-unique-name", "xyz-ui");
        // return new
        // AppConfigurationEntry("examples.security.providers.authentication.Simple.Simple.SampleLoginModuleImpl",
        // controlFlag, options);
        return new AppConfigurationEntry("com.abc.wls.security.providers.authentication.WLServerLoginModule", LoginModuleControlFlag.REQUIRED, options);
    }

    public AppConfigurationEntry getLoginModuleConfiguration() {
        System.out.println("WLAuthenticationProviderImpl.getLoginModuleConfiguration");
        HashMap options = new HashMap();
        return getConfiguration(options);
    }

    public AppConfigurationEntry getAssertionModuleConfiguration() {
        System.out.println("WLAuthenticationProviderImpl.getAssertionModuleConfiguration");
        HashMap options = new HashMap();
        options.put("IdentityAssertion", "true");
        return getConfiguration(options);
    }

    public PrincipalValidator getPrincipalValidator() {
        return new PrincipalValidatorImpl();
    }

    public IdentityAsserterV2 getIdentityAsserter() {
        return null;
    }

}

WLServerLoginModule.java

package com.abc.wls.security.providers.authentication;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
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.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import weblogic.logging.NonCatalogLogger;
import weblogic.security.principal.WLSGroupImpl;
import weblogic.security.principal.WLSUserImpl;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

public class WLServerLoginModule implements LoginModule {
    private static NonCatalogLogger logger = new NonCatalogLogger("WLServerLoginModule");
    // initial state
    private Subject subject;
    private CallbackHandler callbackHandler;
    private Map<String, ?> sharedState;
    private Map<String, ?> options;
    // the authentication status
    private boolean succeeded = false;
    private boolean commitSucceeded = false;

    // username and password
    private String username;
    private String password;
    Map<String, String> userData = new HashMap<String, String>();
    private final JsonParser jsonParser = new JsonParser();

    private WLSAuthPrincipal userPrincipal;

    public WLServerLoginModule() throws LoginException {
        super();
    }

    public void initialize(Subject subject, CallbackHandler callbackHandler, Map<String, ?> sharedState, Map<String, ?> options) {
        logger.info("WLServerLoginModule.initialize");
        this.subject = subject;
        this.callbackHandler = callbackHandler;
        this.setSharedState(sharedState);
        this.setOptions(options);
        String appName = options.get("app-unique-name").toString();
        logger.info("AppName in WLServerLoginModule: " + appName);
    }

    public boolean login() throws LoginException {
        logger.info("WLServerLoginModule.login");
        if (callbackHandler == null)
            throw new LoginException("Error: no CallbackHandler available " + "to garner authentication information from the user");

        Callback[] callbacks = new Callback[2];
        callbacks[0] = new NameCallback("user name: ");
        callbacks[1] = new PasswordCallback("password: ", false);
        try {
            callbackHandler.handle(callbacks);
            username = ((NameCallback) callbacks[0]).getName();
            char[] tmpPassword = ((PasswordCallback) callbacks[1]).getPassword();
            if (tmpPassword == null) {
                // treat a NULL password as an empty password
                tmpPassword = new char[0];
            }
            password = new String(tmpPassword);
            if (isEmpty(username) || isEmpty(password)) {
                throw new LoginException("User name or password is empty");
            }
        } catch (java.io.IOException ioe) {
            throw new LoginException(ioe.toString());
        } catch (UnsupportedCallbackException uce) {
            throw new LoginException("Error: " + uce.getCallback().toString() + " not available to garner authentication information " + "from the user");
        }

        try {
            if (isValidUser(username, password)) {
                succeeded = true;
            } else {
                succeeded = false;
            }
        } catch (Exception e) {
            logger.error("Post validation exception e: ", e);
            succeeded = false;
        }
        return succeeded;
    }

    private boolean isValidUser(String username, String password) {
        // Your custom validation logic
        return true;
    }


    public boolean commit() throws LoginException {
        logger.info("WLServerLoginModule.commit");
        if (succeeded == false) {
            return false;
        } else {
            // add a Principal (authenticated identity) to the Subject
            // assume the user we authenticated is the SamplePrincipal
            userPrincipal = new WLSAuthPrincipal(username, password, userData);
            if (!subject.getPrincipals().contains(userPrincipal)) {
                // subject.getPrincipals().add(new WLSUserImpl(username));
                subject.getPrincipals().add(userPrincipal);
                logger.info("Custom User principal Added");
            }
            subject.getPrincipals().add(new WLSUserImpl(username));
            subject.getPrincipals().add(new WLSGroupImpl("ABC_USERS"));
            logger.info("Login Module successfully added user principal");
            if (subject != null && subject.getPrincipals() != null) {
                logger.info("All user principals added: " + subject.getPrincipals());
                logger.info("All user principals count: " + subject.getPrincipals().size());
            }
            // in any case, clean out state
            username = null;
            password = null;
            commitSucceeded = true;
            return true;
        }
    }

    public boolean abort() throws LoginException {
        logger.info("WLServerLoginModule.abort");
        if (succeeded == false) {
            return false;
        } else if (succeeded == true && commitSucceeded == false) {
            // login succeeded but overall authentication failed
            succeeded = false;
            username = null;
            password = null;
            userPrincipal = null;
        } else {
            // overall authentication succeeded and commit succeeded, but
            // someone else's commit failed
            logout();
        }
        return true;
    }

    public boolean logout() throws LoginException {
        logger.info("WLServerLoginModule.logout");
        subject.getPrincipals().remove(userPrincipal);
        succeeded = false;
        succeeded = commitSucceeded;
        username = null;
        password = null;
        userPrincipal = null;
        logger.info("Login Module successfully removed user principal after successful logout");
        return true;
    }

    public Map<String, ?> getSharedState() {
        return sharedState;
    }

    public void setSharedState(Map<String, ?> sharedState) {
        this.sharedState = sharedState;
    }

    public Map<String, ?> getOptions() {
        return options;
    }

    public void setOptions(Map<String, ?> options) {
        this.options = options;
    }
}

2) I wanted to have my own Custom AuthPrincipal which extends to WLSAbstractPrincipal and implements WLUser. My case is slightly different, I wanted to store username, password and some more critical information about the user in the AuthPrincipal. So, I create this Custom AuthPrincipal.

3) Now, create a build.xml which will emit this Custom Authentication Principal.

4) After generating jar out of this, I copied it inside {WL_HOME}/server/lib/mbeantypes along with the default Authentication Providers supported by WebLogic.

5) Now, We need to change the default realm that is myrealm. Create new Authentication Provider with any name and select Type of it as the name of the custom Auth Provider that you have created. And make this Auth Provider as REQUIRED one.

Comments