IBM Portal User Management Architecture (PUMA) SPI

Recently I had a chance to dig into IBM Portal User Management Architecture (PUMA) Service and its SPI (Service Provider Interface). It is used to access (find, create, modify and delete) the information about WebSphere Portal users and groups. If your portal is configured to use LDAP, PUMA will automatically connect to your LDAP.

Although the PUMA interfaces are fairly well defined, but I did struggle through few things I was trying to accomplish. Therefore I decide to share the bits of code with everyone.

To use PUMA SPI you need to familiarise yourself with the following 7 interfaces located in wp.user.api.jar

  1. com.ibm.portal.um.User
  2. com.ibm.portal.puma.Group
    The User and Group interfaces returned by the SPI inherit the getObjectID() method from the com.ibm.portal.Identifiable interface of the Model SPI.
  3. The following provider objects are used to access the User and Group objects

  4. com.ibm.portal.um.PumaProfile
    PumaProfile provides methods that provide read only access to Portal User and Group attributes and identifiers. It also provides a method “public User getCurrentUser() throws PumaException” to get details of current logged in user.
  5. com.ibm.portal.um.PumaController
    PumaController provides method to create, modify and delete User and Group profiles.
  6. com.ibm.portal.um.PumaLocator
    PumaLocator provides methods for looking up User and Group objects. The interface can be used to obtain a List of Group objects for all of the groups in which the current user is a member.
  7. Before the portlet can use these provider objects, it must first retrieve the home interface.

  8. com.ibm.portal.um.PumaHome
    PumaHome interface gives access to the Proivder objects (PumaProfile, PumaController and PumaLocator)
  9. com.ibm.portal.um.PagingIterator
    Although PagingIterator may not be required for most applications, but provides useful pagination features while retreiving Users/Groups with PumaLocator.

Here is a utility IBMPumaUtility class that I created to show the usage of above interfaces. Click here to download IBMPumaUtility.java

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.naming.NamingException;

import com.ibm.portal.um.Group;
import com.ibm.portal.um.PagingIterator;
import com.ibm.portal.um.PumaController;
import com.ibm.portal.um.PumaHome;
import com.ibm.portal.um.PumaLocator;
import com.ibm.portal.um.PumaProfile;
import com.ibm.portal.um.User;
import com.ibm.portal.um.exceptions.PumaAttributeException;
import com.ibm.portal.um.exceptions.PumaException;
import com.ibm.portal.um.exceptions.PumaMissingAccessRightsException;
import com.ibm.portal.um.exceptions.PumaModelException;
import com.ibm.portal.um.exceptions.PumaSystemException;

public class IBMPumaUtility {

	private static IBMPumaUtility pumaUtility;
	private PumaHome pumaHome;

	private IBMPumaUtility() throws NamingException {
		javax.naming.Context ctx = new javax.naming.InitialContext();
		pumaHome = (PumaHome) ctx.lookup(PumaHome.JNDI_NAME);
	}

	public static IBMPumaUtility getInstance() throws NamingException {
		if(pumaUtility == null) {
			pumaUtility = new IBMPumaUtility();
		}
		return pumaUtility;
	}

	public User getCurrentUser() throws PumaException {
		//To retrieve current User
		PumaProfile profile = pumaHome.getProfile();
		return profile.getCurrentUser();
	}

	public List<User> getUsersByAttribute(String attributeName, String attributeValue)
		throws PumaSystemException, PumaAttributeException, PumaMissingAccessRightsException {
		//To find Users by attribute
		//(eg. attrName = "cn" and attrValue="Manish")
		PumaLocator locator = pumaHome.getLocator();
		return locator.findUsersByAttribute(attributeName, attributeValue);
	}

	public List<User> findUsersByQuery(String query)
		throws PumaSystemException, PumaAttributeException, PumaMissingAccessRightsException {
		//If you need to search by mutilple attributes, search users by query
		PumaLocator locator = pumaHome.getLocator();
		//String query = "((cn != 'Manish') and (givenName != 'wpsadmin'))";
		return locator.findUsersByQuery(query);
	}

	public Map<String, Object> getUserAttributes(User user)
		throws PumaAttributeException, PumaSystemException, PumaModelException, PumaMissingAccessRightsException {
		List<String> returnAttributes = new ArrayList<String>();
		returnAttributes.add("cn");
		returnAttributes.add("mail");
		PumaProfile profile = pumaHome.getProfile();
		Map<String, Object> values = profile.getAttributes(user, returnAttributes);
		return values;
	}

	public List<Group> findGroupsByAttribute(String attributeName, String attributeValue)
		throws PumaSystemException, PumaAttributeException, PumaMissingAccessRightsException {
		//Just like findUsers you can find Groups by attribute
		PumaLocator locator = pumaHome.getLocator();
		List<Group> groups = locator.findGroupsByAttribute(attributeName, attributeValue);
		return groups;
	}

	public PagingIterator<User> paginationFindUsersByQuery(String query, int resultsPerPage)
		throws PumaSystemException, PumaAttributeException, PumaMissingAccessRightsException {
		//PumaLocator provides method to either return a List of PagingIterator
		//int resultsPerPage = 10;
		PumaLocator locator = pumaHome.getLocator();
		Map<String, Integer> pProperties = new HashMap<String, Integer>();
		pProperties.put(PumaLocator.RESULTS_PER_PAGE, resultsPerPage);
		PagingIterator<User> pIterator = locator.findUsersByQuery(query, pProperties);
		return pIterator;
	}

	public void updateUserAttribute(User user, String attribute, String value)
		throws PumaAttributeException, PumaSystemException, PumaModelException, PumaMissingAccessRightsException {
		PumaController pController = pumaHome.getController();
		//Use PumaController to set or remove User attribute
		Map<String, Object> attributes = new HashMap<String, Object>();
		attributes.put(attribute, value);
		pController.setAttributes(user, attributes);
	}

	public void updateUserAttributes(User user, Map<String, Object> attributes)
	throws PumaAttributeException, PumaSystemException, PumaModelException, PumaMissingAccessRightsException {
		PumaController pController = pumaHome.getController();
		//Use PumaController to set or remove User attribute
		pController.setAttributes(user, attributes);
	}

	public void removeUserAttribute(User user, String attribute)
		throws PumaAttributeException, PumaSystemException, PumaModelException, PumaMissingAccessRightsException {
		List<String> keys = new ArrayList<String>();
		//keys.add("mail");
		keys.add(attribute);

		PumaController pController = pumaHome.getController();
		pController.removeAttributes(user, keys);
	}

	public void removeUserAttributes(User user, List<String> attributes)
		throws PumaAttributeException, PumaSystemException, PumaModelException, PumaMissingAccessRightsException {
		PumaController pController = pumaHome.getController();
		pController.removeAttributes(user, attributes);
	}

}

The code above was successfully tested on WPS 7. I hope this helps and if you require more information do send a message. Cheers!

Tagged , , , , , , ,
  • Jon

    My portal is integrated with LDAP thoguth Sun Access Manager. I tried to get the User object from PUmaprofile but it returns null. This same code works if I dont go through Sun AM. Any idea why that would be or any solution.

    thnx

  • Manish

    Hi Jon,

    This is quite surprising that User object is returned null when going through Sun Access Manager. I haven’t encountered this error because I am not using Sun AM.

    I would like to see the code to find out in what context you are calling pumaProfile.getCurrentUser(). Although I don’t think this will be a coding issue because the same is working without Sun AM. Mail me at email@manishchhabra.com

    Cheers,
    Manish

    • prakash

      hi manish i having dis error :Exception Occuredjava.lang.ClassCastException: com.ibm.wps.services.puma.PumaHomeImpl incompatible with com.ibm.portal.um.PumaHome whenever using ur code…

  • Sunil

    When I try to find a user thorugh uid attribyte in findUsersByAttribute method, it retruns empty userlist.

    import com.ibm.portal.um.PumaController;
    import com.ibm.portal.um.PumaHome;
    import com.ibm.portal.um.PumaLocator;

    Name myjndiname = new CompositeName(PumaHome.JNDI_NAME);
    PumaHome myHome = (PumaHome) ctx.lookup(myjndiname);
    PumaController pController = myHome.getController();
    PumaLocator pLocator = myHome.getLocator();
    List allusers = pLocator.findUsersByAttribute(“uid”, “sometestuser”);

    It returns empty list. Though the userid thats used in query exists in user registry.
    Am I missing any additional configuration?

    1 thing I would like to point here is.
    In portal puma service spi, we no where bind a user to lookup/edit the user registry. So what user is used by puma portal service spi. Does it use annonymous user?

  • Manish

    Hi Sunil,

    I copied your code snippet into my portlet and it seemed to work without any issues. I emailed you the code and hopefully that works for you.

    We do not explicitly bind the user to call PUMA Service, but current user context is used to decide the service access.

    Let me know how you go or if possible provide some more details about your environment? You can email me on email@manishchhabra.com

    Cheers,
    Manish

  • abhishek pdiwa

    hey I have faced the issue regarding accessing PUMA Profile after using the loginService but I am getting the user as null. I dont know why but just want to ask whether any configuration is needed as such for enabling PUMA in loginPortlet???????

    Code snippet-
    I have faced a simple issue regarding using PumaProfile.getCurrentUser() method. I am getting the user as null in it after the LoginService of the portal is called successfully.

    CustomLoginPortlet.java

    public void processAction(ActionRequest request, ActionResponse response) throws PortletException, java.io.IOException {
    LoginService loginService = (LoginService) loginHome.getLoginService(request, response);
    String userId = request.getParameter(FORM_ID);
    String password = request.getParameter(FORM_PASSWORD);

    Map contextMap = new HashMap();
    contextMap.put(LoginService.DO_RESUME_SESSION_KEY, new Boolean(false));
    try {
    loginService.login(userId, password.toCharArray(), contextMap, null);
    } catch (Exception ex) {
    System.out.println(“this login failed with = ” + ex.getMessage());
    ex.printStackTrace();
    }

    try{
    javax.naming.Context ctx = new javax.naming.InitialContext();
    PortletServiceHome psh1 = (PortletServiceHome) ctx.lookup(“portletservice/com.ibm.portal.um.portletservice.PumaHome”);

    com.ibm.portal.um.portletservice.PumaHome service = (com.ibm.portal.um.portletservice.PumaHome) psh1.getPortletService(com.ibm.portal.um.portletservice.PumaHome.class);

    PumaProfile pp = service.getProfile(request);

    PumaLocator pl = service.getLocator(request);
    PumaController pController = service.getController(request);

    User singleUser = pp.getCurrentUser();

    //The single user is getting null in sysout logs
    System.out.println(“Abhishek—->The Current Login user is: ” +singleUser);
    }
    catch(Exception e){
    e.printStackTrace();
    }
    }

    I have a simple logic for logging into the portal application by using Loginservice. There is no issue in LoginService. I have implemented this logic in the processAction() of LoginPortlet. After using login service I have initialized the PUMA controller,profile and locator. Now when I use PumaProfile.getCurrentUser() I am getting the user as null.

    I want to know why is getting null?????????? Have I missed something essential?

    Kindly please help me out in this issue.

    Looking forward for your quick response

  • Manish

    Hi Abhishek,

    Once you call the loginService.login( ) method, portal has to redirect to the authenticated area/page and then you can use PUMA API to access current user details.

    Therefore you will have to move out PumaProfile.getCurrentUser( ) code from processAction( ) and place it in the page it redirects to. Perhaps the doView( ) method of the portlet it opens next? You could use IBMPumaUtility.getInstance( ).getCurrentUser( ).

    This is how I understand and usually implement the code. I know it sucks, but if there is a workaround, I would love to know.

    Regards,
    Manish

  • abhishek pdiwa

    hey thanks manish for your information………..I have tried that thing in portal default.jsp and it is getting the user by using pp.getCurrentUser()……but my application wants the user in the processAction so that i can modify the custom attribute in it which i have added in the attribute list……….I hope you got my point ……..is there any other way of doing it?

  • abhishek pdiwa

    Hey hi manish,

    I have just made another small change to it………I have added portal filters to it and now after pro-processing I have added the PUMA logic which I had used earlier in processAction()…………Still I am not getting the user and it is showing as null.

    PortletFilter.java

    public void doFilter(
    ActionRequest request,
    ActionResponse response,
    FilterChain chain) throws IOException, PortletException {

    System.out.println(“==========Abhishek——>before preprocessing in ACTIONFILTER=============”);
    chain.doFilter(request, response);
    System.out.println(“==========Abhishek——>after preprocessing in ACTIONFILTER==============”);

    PumaProfile pp = service.getProfile(request);

    PumaLocator pl = service.getLocator(request);
    PumaController pController = service.getController(request);

    User singleUser = pp.getCurrentUser();

    //The single user is getting null in sysout logs
    System.out.println(“Abhishek—->The Current Login user is: ” +singleUser);

    .Actually I want to set a user-defined attribute in PUMAController which depends mainly on the type of the user………and after successfully setting it I have to retrieve the same in Portal default.jsp and display to users only certain pages according to the logic written in the user-defined attribute………..

    • Manish

      Hi Abhishek,

      Now I understand the reason why you need to access PUMA API in the processAction( ).

      If you are trying to use PUMA API to retrieve current user immediately after call to LoginService.login( ) you will have to do it differently. Since the page is not redirected, valid session is not bound to the user request.

      What you could do is get user object by calling pumaLocator.findUserByIdentifier( userID ) or pl.findUsersByAttribute(“uid”, userID). Since you have the user id, this should not be an issue.

      A point to note here is that you will have to give Editor access to “Anonymous Portal User” to be able to make calls to PUMA API, otherwise it will return null.

      Cheers,
      Manish

  • abhishek pidwa

    Thanks manish ……..actually I have tried this earlier but I was not sure about the Editor access to Anonymous portal user ………..will update you once it is done

  • abhishek

    Hi manish,

    In reply to your last reply u said that give Editor Access to ‘Anonymous Portal User’. I am confused as to which portlet I should give Editor access. Can u please briefly tell me about that because still I am getting the user as null.

    I have tried the following—->
    1)Resource Persmissions- Users-Editor-Anonymous
    2)Portlets-LoginPortlet-Editor-Anonymous
    3)Users and Group Permissions-Users-Editor-Anonymous Portal user

    Apart from these some random lookouts but still getting user as null.
    kindly help me in this issue

    • Yogesh

      Hi Manish/Abhishek,

      I am facing the exact issue as above, is there a workaround to get User details using PUMA API immediately after the login() in processAction.
      Working on Portal 8.

      Appreciate the help!

      Regards
      Yogesh

  • Ed

    I am trying to access user attributes through PUMA SPI through portal jndi lookup in the theme of the “myportal” area of the site, but always getting back uid attribute with value “anonymous portal user”. If I use portlet home using portlet jndi home lookup, I can get current user. I see that there are some fix packs for portal 7 for similar issue but I am on 7.0.0.1 so it should be fixed. Any reason why I should not be able to get current user once authenticated and within site?

    utility class:
    public static IUserUtil getInstance() throws NamingException {
    if (pumaUtility == null) {
    pumaUtility = new PortalUserUtil();
    }

    if (pumaUtility.getPumaHome() == null) {
    try {
    Context context = new InitialContext();
    pumaUtility.setPumaHome((PumaHome) context.lookup(PumaHome.JNDI_NAME));
    } catch (NamingException e) {
    log.error(“Caught exception looking up PumaHome using JNDI Name: ”
    + PumaHome.JNDI_NAME + ” exception: ” + e.getMessage(), e);
    throw new RuntimeException(e);
    }
    }

    return pumaUtility;
    }

    public User getCurrentUser() throws PumaException {
    // To retrieve current User
    PumaProfile profile = getPumaHome().getProfile();
    return profile.getCurrentUser();
    }

    public Map getUserAttributes() {
    Map userAttribMap = null;
    try {
    userAttribMap = getUserAttributes(getCurrentUser());
    } catch {… }
    return userAttribMap;
    }

    public Map getUserAttributes(User user)
    throws PumaAttributeException, PumaSystemException,
    PumaModelException, PumaMissingAccessRightsException {
    PumaProfile profile = getPumaHome().getProfile();
    Map values = profile.getAttributes(user,
    profile.getDefinedUserAttributeNames());
    …etc

    theme JSP Page:
    IUserUtil userUtil = PortalUserUtil.getInstance();
    Map userAttribMap = userUtil.getUserAttributes();

    • Ed

      I figured out my PUMA SPI theme problem. The way I was including my jsp was causing the code to be run in a separate thread that has no context (my clue https://www-304.ibm.com/support/docview.wss?uid=swg1PM19097 )

      Here’s what I was doing at the end of my theme jsp:
      <script type="text/javascript" src="”>

      There was no reason for including it this way other than I had been doing it in my other static js file. I changed it to this and things started working:

      • Ed

        Sorry, it got cut off:

        changed from:
        < scr ipt type="text/ javascript" src="”>

        to:

  • Ed

    Lets try again:

    to:

    lessthan+percent+atsign include file=”./js/asa_dynamic_js.jspf” percent+greaterthan

  • ahamed

    Hai Manish,
    i need that wp.user.api.jar file…will u send that jar file to my mail.pls…

    Regards,
    Ahamed Basha S

  • ahamed

    Hai manish,

    Actually my problem is i need to change the user password in tivoli directory,,But when i am using the com.ibm.portal.portlet.service.PortletServiceHome i got the error about this (package is not available or create class or package for it) and can not resolved..So please help me what are the packages needed to done this work..and pls tell me the what are the steps to be follow to done this work..

    • Manish

      com.ibm.portal.portlet.service.PortletServiceHome class is located in wp.pe.api.standard.jar

  • ahamed

    Thank you for your immediate response to my questions about that wp.user.api..

  • ahamed

    i am using websphereportal 6.1

  • ahamed

    websphere portal 6.1.0.1

  • ahamed

    thank u very much…

  • ahamed

    where do i get from the com.ibm.portal.Identifiable …pls..

  • ahamed

    Hai Manish…

    Can any one tell me how to resolve this error?

    The type com.ibm.portal.Identifiable cannot be resolved. It is indirectly referenced from required .class files

    Please…

    • Manish

      Hi Ahamed,

      The class is part of wp.base.jar. I see you have quite a few issues with your library dependencies. You can use Jarminator to find out the jar file that contains your missing classes.

  • ahamed

    thank u..

  • ahamed

    when i am trying to initiate the portletservice home it shows the following error..
    com.ibm.wps.services.portletserviceregistry.home.PortletServiceHomeImpl incompatible with com.ibm.portal.portlet.service.PortletServiceHome
    will u help me …

  • ahamed

    the executed line is :

    javax.naming.Context ctx = new javax.naming.InitialContext();
    portletServiceHome = (PortletServiceHome)ctx.lookup(“portletservice/com.ibm.portal.um.portletservice.PumaHome”);

  • Ankit

    Hi,

    I am using WPS (WebSphere Portal Server) 6.1
    WPS provides its own authentication for any deployed application.
    But I am having a separate authentication application which is used in some other projects also.

    Now if I want to bypass the authentication provided by WPS and want to use my own authentication application in its place then what I have to do for that?

    Will PUMA will be helpful for me or any other configuration I have to do?

  • Oliver

    Thank you for the code, it’s very helpfull.
    But there is something I am wondering: if I use this class in order to get the name of the user group of the user every time he connects to a portlet, would it bee thread safe? I mean if I use the getInstance function of this class to create it, would I have some issues when two people will use the portal at the same time (the second one getting the puma home of the first one)?

  • Santosh

    Hai Manish

    I want to know all groups names using PUMA,By using Which method i will retrieve this thing.

    here i am using the code like

    List s;
    try {
    s = pl.findGroupsByDefaultAttribute(uid);
    Iterator i=s.iterator();
    while(i.hasNext())
    {
    System.out.println(i.next());
    }
    } catch (PumaSystemException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    } catch (PumaMissingAccessRightsException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
    }

    But it does give the answer

    please help me out from this

  • Aditya

    Hi Manish,

    I am having problem in accessing an attribute in LDAP. I am able to manipulate that attribute in LDAP browser (JExplorer), however when i write a mathod in jsp, I get AttributeNotDefinedException Exception.

    The code goes as below.
    =======================================
    List digiPassVar = new ArrayList();
    digiPassVar.add(“digiPass”);
    HashMap digiPassVarMap = (HashMap) pp.getAttributes(user,digiPassVar);
    out.print(digiPassVarMap);
    =======================================

    user – User Principal Object
    pp – Object of PumaProfile

    It gives me error as :-
    /*
    com.ibm.wps.um.exceptions.impl.AttributeNotDefinedExceptionImpl: com.ibm.portal.puma.AttributeNotDefinedException: EJPSG0007E: One of the attribute specified is not defined for this member type.digiPass:
    */

    The code works perfect for other attributes, i am able to fetch other. I have tried to map the value in ‘wimconfig.xml’ as,

    PersonAccount

    but still it doesn’t work. Please help…

    • Manish

      Hi Aditya,

      It should work fine from the code you have provided. I don’t think the problem is in your JSP code.

      Since LDAP shows the “digiPass” property, I guess the problem will be in your portal server settings to read these custom ldap properties. Since I can’t see the whole system. I recommend checking your wkplc.properties file.

      Cheers

  • Nitin

    I have used above code in portlet , but I am exception java.lang.ClassCastException: com.ibm.wps.services.puma.PumaHomeImpl incompatible with com.ibm.portal.um.PumaHome

    we are using websphere portal 7
    Can you please let me know how to resovle

    • prakash

      nitin u got solution for dis… help me out what u did for dis error

      • Nitin

        prakash,

        i have placed user related jar in portlet lib directory which was causing issue, after removing jars form lib folder, its resolved.

        • prakash

          well said.. nitin.. i also did same pblm.. after removing .jar from lib folder its working.. thanks a lot

        • prakash

          hi, nitin i want to access values of custom attributes from LDAP server using puma api.. its possible ..if it is help me out..

  • Dilip

    Hello Manish,

    i need to use findUsersByQuery method of PumaLocator, can you please explain (or) share the infomation about how to construct queries using Xpath

  • Santosh

    Hi Manish,

    I want to if check user exists in a group or not can you help me how to check by using PUMA ?