Pages

Thursday, February 11, 2010

Soa 11g Identity Service and Human Task

Soa 11g has an Identity web service which you can use to retrieve your Human Workflow Groups and Users. In this Blog I will show you how you can use this identity service to add users and groups with their managers ( build a hierarchy ) without the need of an expensive Directory server. In the Human Task you can use this hierarchy for Task Escalation and these groups and users can also be used for the Human Task owner or assignee. Basically the identity service will return all the users and groups of the myrealm weblogic security realm. Too bad building this hierarchy is not possible in the Weblogic Console or Enterprise Manager website.
When you have an LDAP server like Oracle Internet Directory or Active Directory you can use this for the Human workflow groups and users. Make sure that the control type of all the authenticators has the SUFFICIENT value and that is LDAP is the first server in the Authentication providers.
So the first step is to retrieve all the Human Task groups & users. For this we need to call the Identity web service, this WS only runs on the Weblogic soa_server1 server ( so we need to connect to port 8001 and no authorization is required). This information can't be retrieved from the Human Task EJB and the security realm is always jazn.com or just leave it empty.

package nl.whitehorses.soa.human.workflow;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;

import oracle.tip.pc.services.identity.BPMGroup;
import oracle.tip.pc.services.identity.BPMIdentityException;
import oracle.tip.pc.services.identity.BPMIdentityService;
import oracle.tip.pc.services.identity.BPMUser;

public class WorkflowGroupUser {

private String wsurl = "http://localhost:8001";
private String secdomain = "jazn.com";

private BPMIdentityService bpmClient;

private Logger logger = Logger.getLogger(WorkflowGroupUser.class.getName());


public WorkflowGroupUser() {
Map<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, java.lang.String> properties =
new HashMap<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, java.lang.String>();

properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.SOAP_END_POINT_ROOT, wsurl);
bpmClient = WorkflowServiceClientFactory.getSOAPIdentityServiceClient(secdomain,
properties,
logger);
}

public List<BPMGroup> getGroups() throws BPMIdentityException {
List<BPMGroup> groups = bpmClient.searchGroups("name", "*");
return groups;
}

public List<BPMUser> getUsers() throws BPMIdentityException {

List<BPMUser> users = bpmClient.searchUsers("name", "*");
return users;
}


public static void main(String[] args) {
WorkflowGroupUser workflowGroupUser = new WorkflowGroupUser();

try {

for (BPMGroup group : workflowGroupUser.getGroups()) {
System.out.println("groups: " + group.getDisplayName());
}
for (BPMUser user : workflowGroupUser.getUsers()) {
System.out.println("users: " + user.getDisplayName()+" manager " +user.getManager());

}
} catch (BPMIdentityException e) {
e.printStackTrace();
}
}
}

For Security reasons I need to make a Servlet or EJB Session Bean and deploy this to the Weblogic soa server ( not the admin server ). In this Servlet or EJB Session Bean we can retrieve IdentityConfigService and initialize the IdentityStore. In this blog I choose for an EJB Session Bean.

package nl.whitehorses.soa.human.workflow.model;

import java.util.List;

import javax.ejb.Remote;
import javax.ejb.Stateless;

import oracle.security.idm.IMException;
import oracle.security.idm.IdentityStore;
import oracle.security.idm.Property;
import oracle.security.idm.PropertySet;
import oracle.security.idm.Role;
import oracle.security.idm.RoleManager;
import oracle.security.idm.RoleProfile;
import oracle.security.idm.User;
import oracle.security.idm.UserManager;
import oracle.security.idm.UserProfile;

import oracle.tip.pc.services.common.ServiceFactory;
import oracle.tip.pc.services.identity.BPMIdentityException;
import oracle.tip.pc.services.identity.config.BPMIdentityConfigService;
import oracle.tip.pc.services.identity.config.ProviderCfg;
import oracle.tip.pc.services.identity.jps.JpsProvider;


@Stateless(name = "HumanWorkFlowEJB", mappedName = "HumanWorkFlowEJB")
@Remote
public class HumanWorkFlowEJBBean implements HumanWorkFlowEJB {


private BPMIdentityConfigService bpmConfig;
private IdentityStore identityStore;

public HumanWorkFlowEJBBean() throws BPMIdentityException, Exception {

bpmConfig = ServiceFactory.getIdentityConfigServiceInstance();

String realmName = bpmConfig.getDefaultRealmName();
ProviderCfg conf = bpmConfig.getConfiguration(realmName).getProviderCfg("Authorization");

JpsProvider provider = (JpsProvider)JpsProvider.getInstance(conf);
identityStore = provider.getIdentityStore();
System.out.println("default realm: " + realmName);
}


public void addUser(String name, String email,
String manager) throws IMException {
UserManager usrmgr = identityStore.getUserManager();

User user = null;
try {
user = identityStore.searchUser(name);
} catch (IMException e) {
}

if (user != null) {
System.out.println("user " + name + " exists");
} else {
PropertySet pset = new PropertySet();
pset.put(new Property("DESCRIPTION", name));
pset.put(new Property("USER_NAME", name));
pset.put(new Property("USER_ID", name));

char pwd[] = "weblogic".toCharArray();

user = usrmgr.createUser(name, pwd, pset);
user = identityStore.searchUser(user.getName());
UserProfile up = user.getUserProfile();
if ( email != null ) {
up.setBusinessEmail(email);
}
if ( manager != null ) {
User userManager = null;
try {
userManager = identityStore.searchUser(manager);
} catch (IMException ie) {
}
if (manager != null) {
up.setManager(userManager.getUniqueName());
}
}
}
}

public void addGroup(String group,
List<String> members) throws IMException {
RoleManager rolemgr = identityStore.getRoleManager();
Role role = null;
try {
role = identityStore.searchRole(1, group);
} catch (IMException e) {
}
if (role == null) {
role = rolemgr.createRole(group);
}

RoleProfile rolePrf = role.getRoleProfile();

if (members != null && members.size() > 0) {
for (int m = 0; m < members.size(); m++) {
User user = null;
try {
user = identityStore.searchUser((String)members.get(m));
} catch (IMException e) {
}
if (user != null) {
rolemgr.grantRole(role, user.getPrincipal());
}
}
}
}


public void removeUser(String user) throws Exception {
UserManager usrmgr = identityStore.getUserManager();
if (user != null) {
User idtyUser = null;
try {
idtyUser = identityStore.searchUser(user);
} catch (IMException e) {
}

if (idtyUser != null) {
usrmgr.dropUser(idtyUser);
}
}
}

public void removeGroup(String role) throws Exception {
RoleManager rolemgr = identityStore.getRoleManager();
Role idtyRole = null;
try {
idtyRole = identityStore.searchRole(1, role);
} catch (IMException e) {
}

if (idtyRole != null) {
rolemgr.dropRole(idtyRole);
}
}

}

And the Remote interface of this EJB Session Bean.

package nl.whitehorses.soa.human.workflow.model;

import java.util.List;

import javax.ejb.Remote;

import oracle.security.idm.IMException;

@Remote
public interface HumanWorkFlowEJB {

public void removeGroup(String role) throws Exception;
public void removeUser(String user) throws Exception;
public void addGroup(String group, List<String> members) throws IMException;
public void addUser(String name, String email,String manager) throws IMException;

}

This EJB Session needs the bpm-services.jar located in the following folder Soa11gPS1\Oracle_SOA1\soa\modules\oracle.soa.workflow_11.1.1\bpm-services.jar
Add this jar as a library in Weblogic, use the name bpm-services and make sure it is targeted on the soa server and not the admin server.
Add the bpm-services library reference to the weblogic-application.xml deployment descriptor. this file will be added to the META-INF of the ear deployment.

<?xml version = '1.0' encoding = 'windows-1252'?>
<weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-application http://www.bea.com/ns/weblogic/weblogic-application/1.0/weblogic-application.xsd"
xmlns="http://www.bea.com/ns/weblogic/weblogic-application">
<library-ref>
<library-name>bpm-services</library-name>
</library-ref>
</weblogic-application>

Deploy the EJB Session Bean to the Soa server and not to the Admin server.
After this we can add some users with their managers. I will use this EJB test client for this( connect to the SoaSuite server ( port 8001) not the Admin server

package nl.whitehorses.soa.human.workflow;

import java.util.ArrayList;
import java.util.Hashtable;

import java.util.List;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import nl.whitehorses.soa.human.workflow.model.HumanWorkFlowEJB;

public class HumanWorkFlowEJBClient {
public static void main(String [] args) {
try {
final Context context = getInitialContext();
HumanWorkFlowEJB humanWorkFlowEJB = (HumanWorkFlowEJB)context.lookup("HumanWorkFlowEJB#nl.whitehorses.soa.human.workflow.model.HumanWorkFlowEJB");

humanWorkFlowEJB.addUser("admin1", "admin1@wh.nl", null);

humanWorkFlowEJB.addUser("admin3", "admin3@wh.nl", "admin1");

List<String> members = new ArrayList();
members.add("admin3");
members.add("admin1");

humanWorkFlowEJB.addGroup("App-MHS-Admin_2", members);


} catch (Exception ex) {
ex.printStackTrace();
}
}

private static Context getInitialContext() throws NamingException {
Hashtable env = new Hashtable();
// WebLogic Server 10.x connection details
env.put( Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory" );
env.put(Context.PROVIDER_URL, "t3://localhost:8001");
return new InitialContext( env );
}
}

These Human Taks users & groups are normal Weblogic users & groups and are visible in the Weblogic Security Realm ( Weblogic Console ).

22 comments:

  1. sorry but i dont think this sample very useful...

    ReplyDelete
  2. You are stressing to only deploy to the SOA server and not the Admin Server. Why is that? Does the IdentityService not work on the AdminServer?

    ReplyDelete
  3. Hi,

    there is no identity service on the adminServer. So it won't work. In PS3 you can do a developer install of SOA Suite where everything is on the adminserver. This is the exception to this.

    thanks

    ReplyDelete
  4. Edwin - thanks for the prompt reply. Ok, that is how we are setup in non-prod environments (using PS3), with everything on a single server. But for some reason, our identity service is not 'finding' LDAP users in those environments but does when we have the SOA server broken out. The ADProvider is setup identically in either environment. So I was wondering if there was a limitation of the IdentityService to use LDAP if it were running within the AdminServer.

    I'll continue to research.

    Thanks!

    ReplyDelete
  5. Hi,

    I think they did this to reduce the memory consumption, you need to find the ear on the soa server which does the identity service and add it manually on your adminserver. then you can connect to this EJB / WS.

    thanks

    ReplyDelete
  6. Hello,

    I need to create a user task where the user can searck for users and groups, and then select some of them.

    I already saw this kind of functionality in many out of the box features in workspace (for instance, identity browse makes something like this) or in the reassign task page on the reassing option in workspace, this really does what Im looking for).

    So my question is: can I get and customize any of this standard pages? Or do I need to create one? If I need to create one, do I need to create such a logic to search ldap like you show here?

    thank you,

    nep

    ReplyDelete
  7. Hi,

    yep, you need to create your own page and query the users / groups

    like in this blogs code.

    public List getGroups()
    public List getUsers()

    this will call the identity service.

    then you can do what you want.

    thanks

    ReplyDelete
  8. Hello,

    thank you for your answer.

    the component I was talking about is IdentityBrowser. Have you sure I cant reuse it? Because it does actually that kind of search. The unique problem is that I would need to overwrite the listner of the ok button to control the process (dont know if it is possible)?

    thank you again

    ReplyDelete
  9. Hi,
    I am able to get bpmAppRole and also the users.Is there any way I can map users to bpmaAppRoles like we do in bpm workspace.In workspace under administration we click on bpmAppRole and add users to it.Can we do the same thing using java api.

    Thanks

    ReplyDelete
  10. Edwin, Could you share any link or details on Integrating an existing Custom Application(Wrapper application over Oracle-HR security Hierarchy) with Oracle Identity Service for workflow assignment?

    ReplyDelete
  11. Edwin, Could you share any link or details on Integrating an existing Custom Application(Wrapper application over Oracle-HR security Hierarchy) with Oracle Identity Service for workflow assignment?
    Thanks

    ReplyDelete
  12. Hi Edwin,

    I am having little trouble with running custom client as webapp. I followed your steps and tested in as an console app and it is working fine. But If I try to run it in debug mode with jdeveloper's embedded server it is returning me no class def found. Any hints?

    ReplyDelete
  13. Hi,

    I think I saw that error once, do you have a stacktrace.

    thanks

    ReplyDelete
  14. Hi Edwin,
    your blog is a great source of valuable information, thank you for sharing!

    Now, i've made a custom client thanks to your hints. All works fine, except app-roles retrieval.

    It works if unit tested, or deploying the client app on tomcat, but fails when the app is deployed on a second domain of soa-suite wls. (i can't deploy my gui-app on the same domain of soa-suite because i've used richfaces that clashes with adf library).

    I get this error: "ERR: ORABPEL-10585 Errore di servizio. Errore interno: errore di servizio in BPMIdentityService nel metodo getGrantedRolesToUser. Vedere il file di log configurato per oracle.soa.services.identity"
    Sorry it's in italian, it fails inkoking the method: getGrantedRolesToUser.
    I can't find anything on server logs...
    Googling i found that this error code is likely associated to misconfiguration on /etc/hosts but this is not the case, i checked.
    Any idea?
    Thanks in advance.

    ReplyDelete
    Replies
    1. Hi,

      For the roles it connects to the identity ws located on the soa suite server
      that is this var private String wsurl = "http://localhost:8001";

      I think you opss is not configured or can't connect to this ws or it tries to use the authenticated user in tomcat which is not known in soa domain.

      can you not add a managed wls server to the soa domain which has not adf lib target to this server. then you don't have security problems when you authenticate.

      thanks

      Delete
  15. Hi Sir
    I face problem regarding to integrate P6 and BPM
    ORABPEL-10585
    BPM service could not connect to BPM Identity service provider for database

    could you help me ?

    ReplyDelete
    Replies
    1. Hi,

      OPSS only works great with LDAP servers and use virtualize property for extra authenticators or add this as first authenticator and put all provider control flag on sufficient.

      thanks

      Delete
  16. Edwin do you know where is configure the users'manager in the weblogic console, or jazn file. I find Attributes but not he hierarchy.

    Thanks in advance.

    ReplyDelete
    Replies
    1. Hi,

      it is located in the weblogic LDAP , you can use a ldap client to connect to the weblogic ldap and change it there.

      you can follow this http://technology.amis.nl/2013/02/05/21036/


      Thanks

      Delete
  17. Edwin,
    I am getting java.lang.NoSuchMethodError: javax.wsdl.Definition.getAllPortTypes()Ljava/util/Map
    while invoking searchUser method . Weblogic is integrated with ActiveDirectory. I am getting BPMdentityService in standlone java .

    Thanks

    ReplyDelete
  18. Never mind , added orawsdl.jar file , it worked .Thanks

    ReplyDelete
  19. Hi Edwin,

    I am getting an error while when I click on the user name link at the top left corner on the BPM Worklist application on Oracle SOA Suite 11.1.1.7. An empty user info pops up. I am using a custom security provider plugin to validate the users and it works fine. But clicking on the link throws an exception. My weblogic has two servers; one admin and the other is a managed soa server. I haven't configured OPSS as well. I came to know from Oracle support that OPSS may be required for multiple server domain environments. Is that really the case? Could you please help in this matter?

    Exception Stacktrace below:
    Refer to the log file that is configured for oracle.soa.services.identity for more details on this error and contact Oracle Support Services
    ".
    Ensure that the soap message is properly formed and has all necessary attributes and elements. Contact Oracle Support Services if error is not fixable.
    ORABPEL-10592

    Identity Service soap error.
    BPMIdentityService encountered soap error in method invoke with fault "Service error.
    Internal Error; Service error occurs in UserImpl in method populateDetails().
    Refer to the log file that is configured for oracle.soa.services.identity for more details on this error and contact Oracle Support Services
    ".
    Ensure that the soap message is properly formed and has all necessary attributes and elements. Contact Oracle Support Services if error is not fixable.

    at oracle.bpel.services.identity.client.IdentityServiceSOAPClient.convertSOAPFaultException(IdentityServiceSOAPClient.java:249)
    at oracle.bpel.services.identity.client.IdentityServiceSOAPClient.invoke(IdentityServiceSOAPClient.java:214)
    at oracle.bpel.services.identity.client.IdentityServiceSOAPClient.lookupUser(IdentityServiceSOAPClient.java:287)
    at oracle.bpel.services.identity.client.AbstractIdentityServiceClient.lookupUser(AbstractIdentityServiceClient.java:187)
    at oracle.bpel.worklistapp.util.WorklistUtil.getCurrentUser(WorklistUtil.java:641)
    at oracle.bpel.worklistapp.beans.view.HomePageView.getUserDisplayName(HomePageView.java:140)
    at oracle.bpel.worklistapp.beans.view.HomePageView.getCurrentUser(HomePageView.java:128)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at javax.el.BeanELResolver.getValue(BeanELResolver.java:293)
    at com.sun.faces.el.DemuxCompositeELResolver._getValue(DemuxCompositeELResolver.java:173)
    at com.sun.faces.el.DemuxCompositeELResolver.getValue(DemuxCompositeELResolver.java:200)
    at com.sun.el.parser.AstValue.getValue(AstValue.java:138)
    at com.sun.el.ValueExpressionImpl.getValue(ValueExpressionImpl.java:206)
    at org.apache.myfaces.trinidad.bean.FacesBeanImpl.getProperty(FacesBeanImpl.java:68)
    at oracle.adfinternal.view.faces.renderkit.rich.GoLinkRenderer.getText(GoLinkRenderer.java:526)
    at oracle.adfinternal.view.faces.renderkit.rich.GoLinkRenderer.encodeAll(GoLinkRenderer.java:123)
    at oracle.adfinternal.view.faces.renderkit.rich.CommandLinkRenderer.encodeAll(CommandLinkRenderer.java:158)
    at oracle.adfinternal.view.faces.renderkit.rich.CommandNavigationItemRenderer.encodeAll(CommandNavigationItemRenderer.java:144)
    at oracle.adfinternal.view.faces.renderkit.rich.NavigationPaneRenderer$NavigationPaneItemRenderer.renderItem(NavigationPaneRenderer.java:2056)

    ReplyDelete