Pages

Thursday, February 18, 2010

Soa 11g Human Task client Authentication

In Soa Suite 10g & 11g you can make your own HumanTask Client ( instead of the Worklist Application ) and integrate this in your own Application. Together with Ronald van Luttikhuizen we made an Human Task Client with works with the Soa Suite 10g & 11g version. The 10g version was a bit tricky because we called the 10g Human Task EJB on an OC4J container from an Weblogic Web application. With Soa Suite 11g I don't have these problems because everything is working on the Weblogic Container. In Soa 11g you have three ways to connect to Human Task Service. Use the local or remote EJB and you can call the soap service. The local EJB option is not acceptable because we need to deploy the customer application on the Soa Suite server else it won't work. With the Soap client I need to configure OWSM on the Weblogic Service and soap is not so fast as the Remote EJB option. This gives a other problem I don't want to use the soa suite weblogic account in the customer application.
The user have to authenticate in the Customer application and this user is also known in the Human Task ( I add the same authenticator in the ADF & Soa Suite server) So why don't we use the same user for the EJB call to the Soa Suite.
To make this works you need to make a domain trust between the two Weblogic domains.
To do this you need to go to the weblogic console and change some domain properties.
First change, enable the "Cross Domain Security Enabled" option.
Save this change and go the advanced options where you need to change the domain credential, this password must match with the Soa suite domain password.


Do this on both domains.

The next step is to create Workflow Client in your application. It will use your Application authentication for this remote EJB, so you don't to have a Soa Suite account in your application code.

String wlsserver = "HumanWorkFlow";
String soaserver = System.getProperty("humantask.url");
String wsurl = "http://"+soaserver;
String t3url = "t3://"+soaserver;
String contextFactory = "weblogic.jndi.WLInitialContextFactory";

String identityDomain = "jazn.com";
IWorkflowContext context = null;

IWorkflowServiceClient workflowServiceClient;
BPMIdentityService bpmClient;


WorkflowServicesClientConfigurationType wscct = new WorkflowServicesClientConfigurationType();

List<ServerType> servers = wscct.getServer();
ServerType server = new ServerType();
server.setDefault(true);
server.setName(wlsserver);
servers.add(server);

RemoteClientType rct = new RemoteClientType();
rct.setServerURL(t3url);

rct.setInitialContextFactory(contextFactory);
rct.setParticipateInClientTransaction(false);

server.setRemoteClient(rct);

workflowServiceClient = WorkflowServiceClientFactory.getWorkflowServiceClient(
WorkflowServiceClientFactory.REMOTE_CLIENT,
wscct,
logger2);

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(identityDomain
, properties
, logger2);


The last step is to acquire and release the Human Tasks on behalf of the application user. Because you don't know the application user password you need to have an account which can do that for the application user. This code can do that for the application user. Make sure that this account don't have too much authorization rights in Weblogic and your application.

String identityUsername = System.getProperty("humantask.user" );
String identityPassword = System.getProperty("humantask.password");

IWorkflowContext contextBehalf = null;

ITaskQueryService taskQueryService = getTaskQueryService();

if ( context == null ) {
System.out.println("HumanWorkflow "+identityUsername+ " context created");
context = taskQueryService.authenticate( identityUsername
, identityPassword.toCharArray()
, identityDomain);
}
contextBehalf = taskQueryService.authenticateOnBehalfOf(context, onBehalfOfUser);

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 ).

Sunday, February 7, 2010

Web service references and Soa configuration plan

With Soa Suite 11g you can off course use web service adapters in your composite application and use a configuration plan to change these reference endpoints, so it can work in production or acceptance environment. In a project I did this for a customer where I used composites as a reference and I just generated a configuration plan.
JDeveloper generates a plan and already did some good work for you by detecting the web service references. We only have to change the location attribute of the ws binding of this reference.
But when I deployed these composite to acceptance, I got deployment errors. Looking at the error in the soa_server1-diagnostic.log file I saw that it is complaining about there are more versions of one XSD. This can be true because the calling composite in development has a higher release than that of acceptance. Somehow there is still a reference to the development enviroment. Analyzing the soa project I saw there are some import references in the composite.xml
and in my mediator.wsdl. I also need to change these endpoints in the just generated configuration plan. First let change the import references of the composite.xml. JDeveloper already generated did some work for you.
Change this from
To this
And now I have to change the component WSDL's and XSD's of this composite application. JDeveloper already generates a wsdlAndSchema element with all your project files. When you add composite references or add components, you probably need to update your configuration plan.
So change this
to this.
and now you can deploy this composite to acceptance or production.