Portal, Express Beta Version 6.1
Operating systems: i5/OS, Linux,Windows |
For resources protected by the portal, IBM® WebSphere® Portal Express uses CORBA credentials and an encrypted LTPA cookie to authenticate users. However, for backend systems that require their own authentication, portlets need to provide some form of authentication to access these remote applications. In order to provide a single sign-on user experience, portlets must be able to store and retrieve user credentials for their particular associated application and use those credentials to log in on behalf of the user. WebSphere Portal Express supports the use of a credential vault where users and administrators can safely store credentials for authentication. Portlets written to extract the user's credentials from the vault can hide the login challenge from the user.
The credential vault provides exactly this functionality. Portlets can use it through the credential vault portlet service (CredentialVaultService). See Portlet services for an overview of portlet services and how to access them. The following sections provide more information about the credential vault.
A vault segment is flagged to be either administrator-managed or user-managed. While portlets (on behalf of a portal user) can set and retrieve credentials in both types of segments, they are permitted to create vault slots only in user-managed vault segments. The following figure shows how administrator-managed vault segments can be distributed among different vault implementations. There is only one user-managed vault segment, and it resides in the default-customization vault provided by WebSphere Portal Express.
Vault Slot Type | Segment type | Shared | Creation through | Secret Sharing |
---|---|---|---|---|
System Slot | administrator-managed | true | Credential Vault administrative portlet | one secret per system - shared among all users and portlets |
Administrative Slot | administrator-managed | false | Credential Vault administrative portlet | one secret per user - shared among all of user's portlets |
Shared User Slot | user-managed | true | Credential Vault portlet service | one secret per user - shared among all of user's portlets |
Portlet Private Slot | user-managed | false | Credential Vault portlet service | one secret per user and portlet entity - not shared among portlets |
The credential vault portlet service returns credentials in form of credential objects. The following are the base interface classes for all credential objects:
Standard portlet APIcom.ibm.portal.portlet.service.credentialvault.credentials.CredentialIBM Portlet API
com.ibm.wps.portletservice.credentialvault.credentials.CredentialWebSphere Portal Express differentiates between passive and active credential objects.
Object userSecret = credential.getUserSecret(); < portlet connects to backend system authenticates using the user's secret > < portlet can use the connection to communicate with the backend application > < portlet takes care of logging at the backend >
// log into the backend system credential.login() // get an authenticated connection to work with URLConnection = credential.getAuthenticatedConnection(); // log out at the back end credential.logout();
Active credential objects allow portlets to trigger authentication to remote servers using standard mechanisms such as basic authentication, HTTP form-based authentication, or POP3 authentication, without obtaining the credential secrets. They can ask the portal to authenticate on their behalf and then use already authenticated connections. From a security point of view the portlets never access the credential secrets and thus there is no risk a portlet could violate any security rules like, for example, storing the secret on the portlet session. While there might not always be an appropriate active credential class available, it is the preferred type of credential object to use.
All credential types that are available within the portal are registered in a credential type registry. WebSphere Portal Express provides a small set of credential types, but additional credential objects can be registered in this registry.
The ActiveCredential and PassiveCredential interfaces inherit from the Credential base interface. The following topics describe the different types of active and passive credential objects provided by WebSphere Portal Express:
Credential objects do not implement java.io.Serializable - they cannot simply be stored in the PortletSession. This is for security reasons. Because the credential classes store the actual credential secret as one of their private attributes, the secret could be found by anyone who has access to the application server session database.
However, you can store a credential object in the PortletSession as long as you ensure sure that it is not serialized in a cluster setup. One way of doing this would be to define a credential container class that stores the actual credential object as a transient member. This container object can then be stored in the PortletSession without any problems, you only have to make sure to check whether the credential object got lost during serialization and in this case retrieve it from the vault again.
Figure 2. Example: Credential object session containerimport com.ibm.portal.portlet.service.credentialvault.credentials.Credential; public class CredentialSessionContainer implements java.io.Serializable { private transient Credential credential = null; public void setCredential(Credential cred) {this.credential = cred;} public Credential getCredential() {return credential;} public boolean hasCredential() {return credential != null;} }
The option you choose depends on how the portlet will be used. Generally, the best solution hides the technical details of the credential vault from users. Additionally, Active Credentials of the type JaasSubjectCredential provide the portlet with the ability to access resources within the same single sign-on domain as the portal. The following are some example scenarios for the use of slots.
A company has an intranet employee portal. Each portal user has a Lotus Notes mail server account and a Lotus Notes mail portlet will be deployed and pre-configured on one of the employee's default portal pages.
Design solution:
The Notes mail portlet needs to store the user's Notes password. As most users will actually use this portlet, the administrator needs to create a "Lotus Notes Credential Slot" for it through the Credential Vault administrative portlet. Using the portlet's configure mode, the administrator sets the vault slot ID for all portlet entries. The portlet allows users to set their personal Notes password in edit mode. The portlet can store each user's password in the credential vault.
If the company would use a Lotus Domino server within the same single sign-on domain as portal, it would also be possible to use an LTPAToken Credential based on the user's JAAS Subject. This credential offers access to the domino server through an authenticated connection that reuses the user's LTPA token.
A company's buying department runs a portal that integrates different legacy applications. One of these applications is a mainframe ordering application that directly connects to the systems of the suppliers. Several employees use the ordering portlet. However, the mainframe application is secured by a single system ID; it does not support several user accounts.
Design solution: The ordering portlet needs to access the ordering application under the system ID. The administrator configures the vault slot ID during portlet deployment. The portal administrator therefore creates a vault slot in an administrator-managed vault segment and marks it as a system credential. The administrator uses the Credential vault portlet to store the ordering system ID and password in this slot. The buying department's employees do not have to be concerned with credentials at all.
An Internet community portal offers, among other features, a mail-federating portlet that can be used by a portal user to collect mail from a number of POP3 mail accounts.
Design solution: The mail federating portlet is just another feature of the community portal and thus is likely to be used only by some of the portal users. Furthermore, it is not clear from the outset how many mail accounts a user wants to federate. Therefore, it does not make sense for the portal administrator to create a vault slot for this portlet. Instead, the portlet provides users with a comfortable configuration in edit mode. Users can add as many POP3 mailboxes as necessary. The portlet creates a vault slot for each of the user's mailboxes in the user-managed vault segment.
Theoretically, a user could configure two instances of the portlet on a page, one for business accounts and one for private mail accounts. Therefore, and because it most likely doesn't make sense to share the user's mail credentials with other portlets, the portlet created vault slots are better marked as portlet private.
This section contains sample code for using the credential vault service. See the Portlet API Javadoc for further information about the methods of the CredentialVaultService.
When you use the portlet wizard in Rational Application Developer to create portlets that use the credential vault portlet service, the wizard generates a SecretManager class for you that handles common tasks. The session bean generated by the wizard includes getters and setters for the secret type and vault slot name. The following examples show code that could be generated for you, depending on your selections, when you create a JSR 168 portlet project.
Note: This code is only a demonstration of how you can use the credential vault. Any code that is generated must be further customized for your specific application requirements.This code is placed in the init() method of the SecretManager and is called from the init() of the main portlet class. See Accessing portlet services for general information about how portlet services are retrieved using JNDI.
Figure 3. Retrieving the credential vault servicepublic static void init(PortletConfig config) throws PortletException { try { if( vaultService == null ) { Context ctx = new InitialContext(); PortletServiceHome cvsHome = (PortletServiceHome)ctx.lookup("portletservice/com.ibm.portal.portlet. service.credentialvault.CredentialVaultService"); if (cvsHome != null) { vaultService = (CredentialVaultService)cvsHome.getPortletService (CredentialVaultService.class); } } } catch (Exception e) { throw(new PortletException("Error on init()", e)); } }
The portlet's processAction() method gets the USERID and PASSWORD parameters on the action request from the edit JSP. If both parameters are not null, then these are used to set the credential.
Figure 4. Getting the credentials on the action requestif( request.getParameter(USER_SUBMIT) != null ) { // Set userId/password text in the credential vault PortletSessionBean sessionBean = getSessionBean(request); if( sessionBean!=null ) { String userID = request.getParameter(USERID); String password = request.getParameter(PASSWORD); // save only if both parameters are set if(userID!=null && password!=null && !userID.trim().equals("") && !password.trim().equals("")) { try { SecretManager.setCredential(request,sessionBean,userID,password); } catch (Exception e) { //Exception Handling } } } }
The setCredential() method in the SecretManager class determines if the portlet can write to the slot and if the slot ID has content. If so, it uses the setCredentialSecretUserPassword() method of the credential vault service to set the credential to the slot.
Figure 5. Setting the credentialpublic static boolean setCredential(PortletRequest portletRequest, PortletSessionBean sessionBean, String userID, String password) throws PortletException { try { if( isWritable(sessionBean) ) { String slotId = getSlotId(portletRequest,sessionBean,true); // create slot if necessary if( slotId != null ) { vaultService.setCredentialSecretUserPassword(slotId,userID,password. toCharArray(),portletRequest); return true; } } } catch( CredentialVaultException e) { //Exception Handling } return false; }
The setCredential() method calls this method to either create a new slot or use an existing, accessible slot.
private static String getSlotId(PortletRequest portletRequest, PortletSessionBean sessionBean, boolean bCreate) throws PortletException { String slotId = null; String slotName = sessionBean.getVaultSlotName(); switch( sessionBean.getSecretType() ) { case SECRET_PORTLET_PRIVATE_SLOT: PortletPreferences prefs = portletRequest.getPreferences(); String prefsKey = ".slot."+portletRequest.getRemoteUser()+". "+slotName; slotId = prefs.getValue(prefsKey,null); if( slotId==null && bCreate ) { slotId = createNewSlot(portletRequest,slotName, true); // create private slot if( slotId != null ) { try { prefs.setValue(prefsKey,slotId); prefs.store(); } catch( Exception e ) { throw(new PortletException("Error on PortletPreferences. store()", e)); } } } break; case SECRET_SHARED_SLOT: try { Iterator it = vaultService.getAccessibleSlots(portletRequest); while( it.hasNext() ) { CredentialSlotConfig config = (CredentialSlotConfig)it.next() ; //searches for shared resource name if( config.getResourceName().startsWith(slotName ) ) { slotId = config.getSlotId(); break; } } if( slotId==null && bCreate ) slotId = createNewSlot(portletRequest,slotName,false); // create shared slot } catch( CredentialVaultException e) { // exception handling goes here } break; default: slotId = slotName; break; } return slotId; }
StringBuffer userId = new StringBuffer(""); StringBuffer password = new StringBuffer(""); try { SecretManager.getCredential(request,sessionBean,userId, password); } catch( Exception e ) { getPortletContext().log("Exception on SecretManager.getCredential(): "+e.getMessage()); } request.setAttribute(USERID,userId.toString()); request.setAttribute(PASSWORD,password.toString());The getCredential() method of SecretManager handles UserPasswordPassiveCredential types. The type of credential is set as an Integer in the portlet preferences. The getCredential() method of the credential vault service is used to obtain the credentials stored in the slot. Figure 8. getCredential() method
public static void getCredential(PortletRequest portletRequest, PortletSessionBean sessionBean, StringBuffer userid, StringBuffer password) throws PortletException { try { String slotId = getSlotId(portletRequest,sessionBean,false); if( slotId != null ) { UserPasswordPassiveCredential credential = (UserPasswordPassiveCredential)vaultService.getCredential (slotId,CredentialTypes.USER_PASSWORD_PASSIVE,new HashMap(),portletRequest); if( credential != null) { userid.append(credential.getUserId()); password.append(String.valueOf(credential.getPassword())); } } } catch( CredentialVaultException e) { // exception handling goes here } }
The following is an example of an IBM portlet that uses a userid/password credential to log into a Web application that is secured with an HTTP form-based login page. The example shows how the CredentialVaultService is used to read a credential from the vault and how to use this credential. It does not show how in the portlet's edit mode the portlet queries the user for his/her secret and stores it in the vault. This example is incomplete and should not be pasted into working code.
Figure 9. Example: Portlet using the CredentialVault PortletService with a HttpFormBasedAuthCredentialimport org.apache.jetspeed.portlet.service.PortletServiceException; import com.ibm.portal.portlet.service.credentialvault.CredentialVaultService; import com.ibm.portal.portlet.service.credentialvault.CredentialSecretNotSetException; import com.ibm.portal.portlet.service.credentialvault.credentials. HttpFormBasedAuthCredential; ... public void doView (PortletRequest request, PortletResponse response) throws PortletException, IOException { // get output stream and write out the results PrintWriter writer = response.getWriter(); // get the CredentialVault PortletService PortletContext context = this.getPortletConfig().getContext(); CredentialVaultService service = (CredentialVaultService) context.getService(CredentialVaultService.class); // retrieve slotId from persistent portlet data String slotId = (String) request.getData().getAttribute("VaultTestSlotID"); if (slotId == null) { writer.println("<h2>Credential not found. Please set it in the edit mode! </h2>"); return; } // bundle the credential config data mostly based on the backend application's login form HashMap config = new HashMap(); config.put( HttpFormBasedAuthCredential.KEY_USERID_ATTRIBUTE_NAME, "j_userid"); config.put( HttpFormBasedAuthCredential.KEY_PASSWORD_ATTRIBUTE_NAME, "j_password"); config.put( HttpFormBasedAuthCredential.KEY_LOGIN_URL, "EAI.yourco.com/myapp/ j_security_check"); config.put( HttpFormBasedAuthCredential.KEY_LOGOUT_URL,"EAI.yourco.com/myapp/ quit.jsp"); config.put( HttpFormBasedAuthCredential.KEY_USE_AUTH_COOKIES, Boolean.TRUE); List formData= new ArrayList(); formData.add("action=Login"); // add the POST data that the form's login button will generate (name=value) config.put( HttpFormBasedAuthCredential.KEY_FORM_DATA, formData); // get the actual credential from the credential vault HttpFormBasedAuthCredential credential; try { credential = (HttpFormBasedAuthCredential) service.getCredential(slotId, "HttpFormBasedAuth", config, request); } catch (PortletServiceException serviceExc) { writer.println("<h2>Credential vault error, please contact your admin! </h2>"); return; } catch (CredentialSecretNotSetException userExc) { writer.println("<h2>Credential not set. Please set it in the edit mode! </h2>"); return; } try { // use credential object to log in at the backend server credential.login(); // get an authenticated connection to the backend server and send the actual request connection = credential.getAuthenticatedConnection("EAI.yourco.com/request.jsp"); // Work with the connection: send an HTTP GET or POST and evaluate the response [...] // your business code // use credential object to log out at the backend server credential.logout(); } catch (IOException exc) { writer.println("<h2>Single-sign-on error, login at backend failed! </h2>"); return; } // get output stream and write out the results PrintWriter writer = response.getWriter(); }
WebSphere Portal Express supports plugging in different vault adapters for the storage and retrieval of credentials. The default vault adapter that ships with WebSphere Portal Express stores user credentials in the portal configuration database. By default, the passwords are only obfuscated, but not encrypted.
See the How to change the encryption of the credential vault in WebSphere Portal Express technote for information on how to extend the default vault adapter to use your own encryption code for encrypting and decrypting the passwords.
Parent topic: Understanding the basics