×
[searchandfilter taxonomies="search"]

PeopleSoft telephony two factor authentication

By Chris Heller • July 7, 2008

(update : check out demo 4 in the Flash demo for our ERP Firewall to see some additional real world examples of multi-factor authentication for PeopleSoft)

At the end of the previous blog entry on PeopleSoft telephony integration, we were able to initiate telephone calls from within PeopleCode to an end user and prompt them to enter a PIN code so that we could authenticate them. We could even see in the logs whether the user typed the correct PIN on their phone or not.

What we didn’t cover was how we can figure out in PeopleCode whether the user typed the correct PIN or not. If we can’t do that, then there’s not much point to the whole exercise, eh? 🙂

To make a long story less long, you can’t (easily) find out the answer with just PeopleCode. There are a couple of reasons for this. One was hinted at yesterday; the Asterisk Manager API call origination only allows you to get the call going. Once the call has been successfully initiated, then the call origination reports success. Even if the user does not answer the phone, the call itself was successfully originated.

If you want to find out what happened, then you have to use another part of the Asterisk Manager API, the events API. That allows you to listen to various events happening within Asterisk. So after we originate the call, there will be a series of events triggered (Dial events, Hangup events, Callerid events, etc.) that we can use.

The good news is that the Asterisk-Java library that we’re using has great support for the events API. There are Java classes for all of the different events that occur within the Asterisk server. For example, here’s someone else’s sample Java code of catching the Dial event and using that for screenpops.

In order to register which events that you are interested in though, the Asterisk-Java library require you to implement a Java interface called ManagerEventListener. Implementing interfaces is no big deal when you’re writing Java code; it’s just a list of methods that you have to provide the actual code for. However PeopleCode can’t implement Java interfaces, so we’re going to need to write some Java code ourselves.

In order to make this work, I needed to create two separate Java classes. The first one is called PSoftLoginManager. In addition to implementing the ManagerEventListener interface, I moved the PeopleCode logic for doing the call origination into this class as well.

The other class that I needed to create is called PSoftLoginEvent and it extends the delivered UserEvent class. The Authenticate command that we are using in Asterisk does not actually create any events for us to listen to. However, Asterisk supports the notion of user defined events, so we can use that. At the moment the PSoftLoginEvent includes the PeopleSoft user ID and whether the login attempt was successful or not.

The Java code wasn’t too bad. 100+ lines or so, but let’s look at how the PeopleCode looks now first. All of the variable declarations at the top stay the same, but the rest of the code is now just

Local JavaObject &loginMgr = CreateJavaObject("com.greysparling.asterisk.PSoftLoginManager", &host, &user, &pswd);
Local JavaObject &loginEvt = &loginMgr.challengeUser(&userID, &phone, &pinCode, &channel, &context, &exten);
If &loginEvt.isSuccess() Then
Warning ("Successfully validated user " | &userID);
Else
Warning ("Off with " | &userID | "'s head!");
End-If;



We create our PSoftLoginManager object with the Asterisk server information, and then call challengeUser to initiate the call and get the result back. Instead of having challengeUser return a binary result of success or not, I return the actual PSoftLoginEvent object itself which can be queried for success or failure. That keeps things easy later if we need to expose any additional data back from the Asterisk side.

On the Asterisk configuration side, we need a small change in order to trigger our login event.

[challenge-psft-user]
exten = 7189,1,Answer()
exten = 7189,2,Playback(vm-intro)
exten = 7189,3,NoOp(Authenticating user ${PSFTUSERID})
exten = 7189,4,Authenticate(${PIN},j)
exten = 7189,5,NoOp(Successful login ${PSFTUSERID})
exten = 7189,6,UserEvent(PSoftLogin|userId: ${PSFTUSERID}|result: SUCCESS)
exten = 7189,7,Hangup()
exten = 7189,105,NoOp(Unsuccessful login ${PSFTUSERID})
exten = 7189,106,UserEvent(PSoftLogin|userId: ${PSFTUSERID}|result: FAILURE)
exten = 7189,107,Hangup()



The main difference is the use of the Asterisk UserEvent command. That takes the name of our login event as it’s first parameter. The additional parameters are the ones that we have defined for our event. The Asterisk-Java library will automatically map these extra parameters that we have defined into the appropriate setter calls on the Java PSoftLoginEvent object (e.g. userId maps to setUserId() ).

Now when we run the App Engine program, here’s what the Asterisk console looks like.

    -- Executing [7189@challenge-psft-user:1] Answer("IAX2/123456789-2", "") in new stack
-- Executing [7189@challenge-psft-user:2] Playback("IAX2/123456789-2", "gs-demo-login") in new stack
-- Playing 'gs-demo-login' (language 'en')
-- Executing [7189@challenge-psft-user:3] NoOp("IAX2/123456789-2", "Authenticating user PTDMO") in new stack
-- Executing [7189@challenge-psft-user:4] Authenticate("IAX2/123456789-2", "4554|j") in new sta ck
-- Playing 'agent-pass' (language 'en')
-- Playing 'auth-thankyou' (language 'en')
-- Executing [7189@challenge-psft-user:5] NoOp("IAX2/123456789-2", "Successful login PTDMO") in new stack
-- Executing [7189@challenge-psft-user:6] UserEvent("IAX2/123456789-2", "PSoftLogin|userId: PTDMO|result: SUCCESS") in new stack
-- Executing [7189@challenge-psft-user:7] Hangup("IAX2/123456789-2", "") in new stack
== Spawn extension (challenge-psft-user, 7189, 7) exited non-zero on 'IAX2/123456789-2'
-- Hungup 'IAX2/123456789-2'



You can see the addition of the UserEvent here, plugged in with the name of our event, the PTDMO user and the result value of SUCCESS. Since this event is what our challengeUser function returns to PeopleCode, we can now properly do the two factor authentication from PeopleSoft.

Here’s the Java code for the PSoftLoginEvent class. The things that are worth noting is that it extends the org.asteriskjava.manager.event.UserEvent class, and that it has setters for the values that we want to receive. It appears as though you can only get strings from the Asterisk side, so I adopted the convention that result has to be the string SUCCESS for a successful login.

package com.greysparling.asterisk;

import org.asteriskjava.manager.event.UserEvent;

public class PSoftLoginEvent extends UserEvent {

private String userId;
private String result;

public PSoftLoginEvent(Object source) {
super(source);
}

public String toString() {
return "PSoftLogin for " + getUserId() + " (" + result + "),"
+ super.toString();
}

public boolean isSuccess() {
return "SUCCESS".equals(result);
}

public void setResult(String result) {
this.result = result;
}

public String getUserId() {
return userId;
}

public void setUserId(String userId) {
this.userId = userId;
}

private static final long serialVersionUID = 1L;

}


Here is the Java code for the PSoftLoginManager class. This is a little bit more complex, but not too bad. We implement the ManagerEventListener class so that the Asterisk-Java library knows we want to receive events. That means that we have to provide an “onManagerEvent” method.

Our implementation of onManagerEvent is pretty simplistic – we just check if the event is a PSoftLoginEvent, and whether the user ID in that event matches the one that we just sent. A more robust implementation would use some unique IDs for matching up the exact logins; not just check the user ID. We’d also want to check for other related events such as the user hanging up without even trying to enter the PIN code.

Another thing worth pointing out here is that in the Asterisk-Java library, event notifications come in on a different thread. PeopleCode only runs single threaded, but when you are calling Java from within PeopleCode, there may be multiple threads running from within the Java Virtual Machine.

In this demo implementation, we’re cheating a bit by just sleeping on the main thread while we wait for the login event to come back. It works just fine for our purposes here, but it’s definitely not production ready code. We’d probably also want to have some sort of a connection pool instead of logging in to the Asterisk server on every request.

package com.greysparling.asterisk;

import java.io.IOException;
import java.util.HashMap;

import org.asteriskjava.manager.AuthenticationFailedException;

import org.asteriskjava.manager.ManagerConnection;
import org.asteriskjava.manager.ManagerConnectionFactory;
import org.asteriskjava.manager.ManagerEventListener;
import org.asteriskjava.manager.TimeoutException;

import org.asteriskjava.manager.action.OriginateAction;
import org.asteriskjava.manager.event.ManagerEvent;
import org.asteriskjava.manager.response.ManagerResponse;

public class PSoftLoginManager implements ManagerEventListener {

private ManagerConnection connection;
private boolean keep_running = true;

private String userId;
private PSoftLoginEvent event;

static final String USERID = "PSFTUSERID";

static final String PIN = "PIN";

public PSoftLoginManager(String host, String user, String pswd) throws
AuthenticationFailedException, TimeoutException, IOException {

ManagerConnectionFactory factory =
new ManagerConnectionFactory(host, user, pswd);

connection = factory.createManagerConnection();
connection.addEventListener(this);

connection.registerUserEventClass(PSoftLoginEvent.class);
connection.login();
}

public PSoftLoginEvent challengeUser(String userId, String phone,
String pinCode, String channel, String context, String exten)

throws InterruptedException, TimeoutException, IOException {

this.userId = userId;
this.event = null;

HashMap vars = new HashMap();

vars.put(USERID, userId);
vars.put(PIN, pinCode);

OriginateAction action = new OriginateAction();
action.setChannel(channel + "/" + phone);

action.setContext(context);
action.setVariables(vars);
action.setExten(exten);

action.setPriority(new Integer(1));

ManagerResponse response = connection.sendAction(action, 30000);

System.out.println(response.getResponse());

while (keep_running)
Thread.sleep(100);

return this.event; // caller can check isSuccess()

}

public void onManagerEvent(ManagerEvent event) {
if (event instanceof PSoftLoginEvent) {

PSoftLoginEvent login_event = (PSoftLoginEvent)event;
System.out.println("Login event: " + login_event);

if (userId.equals(login_event.getUserId())) {
System.out.println("Matched user");
this.event = login_event;
this.stop();
}

else {
System.out.println("User was " + login_event.getUserId());
}
}
else {
System.out.println("Event: " + event);
}
}

public void stop() {
keep_running = false;
}

}



So there you have it. We’ve successfully made phone calls from within PeopleSoft, challenged user for a PIN code, and take action accordingly. Everything that we need for two factor authentication of PeopleSoft users, and our total cost is still under one dollar.

(updated with syntax highlighting)

Labels: , , , , ,

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

PeopleSoft IVR Integration the easy way

By Chris Heller • July 6, 2008
Yesterday’s blog post on Java 5 and PeopleTools 8.49 (and my high hopes that it would have fixed something that been annoying me) didn’t have a happy ending. PeopleTools doesn’t (yet) take advantage of the some of the Java 5 features, so we have to stick with some workarounds for the time being. PeopleTools 8.49 using Java 5 does provide me another good new thing to work on though. The open source Asterisk-Java library requires Java 5 at a minimum, but with PeopleTools 8.49, that’s OK. What the library provides is a good Java level interface into the open source Asterisk telephony platform. There’s tons of material about Asterisk available, both online and printed books, so this blog post won’t go into too much background on it. If you want to follow along though, you can’t go wrong with the different downloadable Asterisk appliances out there (PBX in a Flash, AsteriskNOW, etc.). This blog post is actually sort of a lead-in for one of the sessions that I submitted for this year via Mix was called “Telephony Integration with PeopleSoft on the cheap”. I don’t know whether that session will actually make it in for OpenWorld (there are an astounding number of sessions that have been submitted via Mix), but it’s a good topic, so I thought I’d start blogging it. For this blog post, what we want to do is be able to initiate a phone call to the end user from PeopleSoft, prompt them for a PIN code, and take action in PeopleSoft depending on whether they were successful or not. We may want to do this as part of the initial signon process for two-factor authentication of your PeopleSoft users, or we might tie this in with some business logic (e.g. be really sure who is sending off a large wire transfer). For the business logic use case, we recommend that you utilize an application firewall to simplify the configuration and maintenance of it. Our own ERP Firewall for PeopleSoft product has a demonstration on our product page of doing 2-factor authentication using Instant Messaging technology (a similar technique to that used here). For testing, I always like to whip up an App Engine test program for playing around. Let’s take a look at our first attempt at the code, then we’ll look at the Asterisk configuration.
REM Connectivity to Asterisk server. ;
REM Assumes that &user has been granted access to Asterisk Manager API;
Local string &host = "192.168.1.200";
Local string &user = "asterisk_user_id";
Local string &pswd = "asterisk_password";

REM Asterisk information for how we will be originating phone calls;
Local String &voip_provider_acct = "123456789</code><code>";
Local string &channel = "IAX2/" | </code><code>&voip_provider_acct</code><code>;
Local string &context = "challenge-psft-user";
Local string &exten = "7189";

REM The phone number that we will be calling;
REM This would normally come out of the user's profile;
Local string &phone = "15105551212";

REM The PeopleSoft userID of the person to call;
Local string &userID = "PTDMO";

REM This PIN could be autogenerated or stored in the DB;
Local string &pinCode = "4554";


REM ******** No more user variables below here *********** ;
Local string &base = "org.asteriskjava.manager.";
Local JavaObject &connectFactory = CreateJavaObject(&base | "ManagerConnectionFactory", &host, &user, &pswd);

Local JavaObject &connection = &connectFactory.createManagerConnection();
&connection.login();

Local JavaObject &originateAction = CreateJavaObject(&base | "action.OriginateAction");
Local JavaObject &vars = CreateJavaObject("java.util.HashMap");
&vars.put("PIN", &pinCode);
&vars.put("PSFTUSERID", &userID);
&originateAction.setChannel(&channel | "/" | &phone);
&originateAction.setContext(&context);
&originateAction.setVariables(&vars);
&originateAction.setExten(&exten);
&originateAction.setPriority(CreateJavaObject("java.lang.Integer", 1));

Local JavaObject &originateResponse = &connection.sendAction(&originateAction, 30000);
Warning (&originateResponse.getResponse());
After setting up a bunch of variables that we’ll need, the code begins by connecting to the Manager API of our Asterisk server. We’ll need a user/password here that has appropriate access to the Manager API; there probably won’t be any in your default install of Asterisk so you’ll need to add one. This is not an end-user account though; it’s a service account that should be treated with the appropriate security. The other configuration note here is that the Manager API lets you limit access with an IP address range for each user, so in addition to having a strong password, you should limit the user to only be able to connect from your PeopleSoft servers. After that, the code creates a call origination object. The Asterisk Manager API allows us to originate a call between a channel and an extension that is the Asterisk dialplan. A channel can be a lot of things (different Voice over IP protocols, regular phone network, etc.). In this case, I’m using a IAX2 channel that I’ve previously defined in Asterisk to use a VoIP (Voice over Internet Protocol) connection to the outside world. IAX2 is an Asterisk specific protocol similar to the more commonly known standard SIP (for example, both Oracle and BEA have SIP servers). The nice thing is that you don’t need to know too much about the channel details for experimentation purposes though. For example, for this test, I use a pay-by-the-drip VoIP provider called CallWithUs. There’s other providers out there (and we use some of those also), but CallWithUs have a nice web based provisioning system. Sign up through their webpage, send ’em some money, and you’re all set. You then supply the info that they give you to Asterisk, so when you ask Asterisk to make a call, it can send it along through CallWithUs, who then connect the call. If you’re using one of the Asterisk appliances, then you can probably get your first call happening in 30-60 minutes or so. The nice thing about doing it this way is that this can all be running on standard computer equipment. Everything that we’re doing is software based, and CallWithUs deal with the part of actually connecting to “real” phone system. As part of the channel definition for the Asterisk Manager API, we add the end-user’s phone numb er to our “dial string”, which Asterisk sends off to CallWithUs, who make the phone call to the end user. That’s one half of the call equation; my mobile phone is now ringing! Here’s what we see in the Asterisk console with the logging turned up.
  == Parsing '/etc/asterisk/manager.conf': Found
== Manager 'asterisk_user_id' logged on from 192.168.1.11
-- Call accepted by 64.85.163.184 (format ulaw)
-- Format for call is ulaw
As soon as I answered the phone, then the Asterisk console shows
      > Channel IAX2/123456789-2 was answered.
If you’ll remember our original requirements, we needed some programmatic validation of a PIN code through the phone. The cool thing about Asterisk is that there are tons of built-in options for how you handle a call coming in. Here’s a snippet from the Asterisk configuration. This defines what Asterisk calls a “context” and what to do with a call into that context for extension 7189. The name of the context and the extension don’t really matter; we just need for our PeopleCode to match up with Asterisk has.
[challenge-psft-user]
exten => 7189,1,Answer()
exten => 7189,2,Playback(gs-demo-login)
exten => 7189,3,NoOp(Authenticating user ${PSFTUSERID})
exten => 7189,4,Authenticate(${PIN},j)
exten => 7189,5,NoOp(Successful login ${PSFTUSERID})
exten => 7189,6,Hangup()
exten => 7189,105,NoOp(Unsuccessful login ${PSFTUSERID})
exten => 7189,106,Hangup()
One thing that may be a little confusing here is that we’re using Asterisk from two sides. One is our code that is calling the Asterisk Manager API that is initiating the call between my mobile phone and this context/extension definition within Asterisk. However, the Manager API is just initiating the call and then it’s done. The context/extension definition within Asterisk then says what to do with the call to this context/extension. Line 1 of the context/extension definition says to Answer the call. Line 2 plays a message that explains what is happening. On my mobile phone, I hear “This is the Grey Sparling demo login. “. Line 3 just logs what is happening on the Asterisk console. We use one of the variables that was set in the PeopleCode side so that we can match up calls with the PeopleSoft user accounts. Line 4 is a builtin Asterisk command to challenge the user to type in a PIN code. Here we’re using the PIN code that was set from the PeopleCode side. There are a series of voice prompts already delivered in Asterisk that get played as part of this as well as Asterisk “listening” for the DTMF tones from the buttons on the phone being pushed. You can roll your own handling of this within Asterisk (speech recognition anyone?), but the Authenticate command has a lot of built-in functionality for free so we make use of that. One strange thing worth mentioning here is the “j” parameter after the PIN code. That tells Asterisk to jump “+101” if the command fails (it continues on the next line if successful). It’s a bizarre form of GOTO (note that there are other ways of adding logic to Asterisk though). Then we log what happened on the console (depending on whether we get the right PIN code or not) and then hangup the phone. Here’s what the Asterisk console looks like with an invalid login.
    -- Executing [7189@challenge-psft-user:1] Answer("IAX2/123456789-2", "") in new stack
 -- Executing [7189@challenge-psft-user:2] Playback("IAX2/123456789-2", "gs-demo-login") in new stack
 --  Playing 'gs-demo-login' (language 'en')
== Manager 'asterisk_user_id' logged off from 192.168.1.11
 -- Executing [7189@challenge-psft-user:3] NoOp("IAX2/123456789-2", "Authenticating user PTDMO") in new stack
 -- Executing [7189@challenge-psft-user:4] Authenticate("IAX2/123456789-2", "4554|j") in new stack
 --  Playing 'agent-pass' (language 'en')
 --  Playing 'auth-incorrect' (language 'en')
 --  Playing 'auth-incorrect' (language 'en')
 -- Executing [7189@challenge-psft-user:105] NoOp("IAX2/123456789-2", "Unsuccessful login PTDMO") in new stack
 -- Executing [7189@challenge-psft-user:106] Hangup("IAX2/123456789-2", "") in new stack
== Spawn extension (challenge-psft-user, 7189, 106) exited non-zero on 'IAX2/123456789-2'
 -- Hungup 'IAX2/123456789-2'
We can see that in this instance I didn’t type in the PIN code correctly, so I wouldn’t have been granted access. Here’s the relevant bits from the Asterisk console of a successful login.
    --  Playing 'agent-pass' (language 'en')
  --  Playing 'auth-thankyou' (language 'en')
  -- Executing [7189@challenge-psft-user:5] NoOp("IAX2/123456789-3", "Successful login PTDMO") in new stack
  -- Executing [7189@challenge-psft-user:6] Hangup("IAX2/123456789-3", "") in new stack
But how do we find that out within the PeopleCode side so that we can actually take action? That will have to wait until part 2 One final note here. I mentioned that CallWithUs is pay by the drip. One reason that is important is because the pay by the drip VoIP providers are typically more open to initiating multiple calls at once (since you’re not paying a flat fee), which is something that you’d need for doing this for authenticating users. Once you’re comfortable with doing this sort of thing, then you might want to get your own internal phone folks involved, but since this sort of thing is fairly uncommon at this point, you’ll probably be on your own for your initial experiments in this. The nice thing is that it’s fairly cheap though. For each call to my mobile phone that was initiated while testing this I paid US$0.0125. So you get 80 login attempts for a buck 🙂

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

International Rollouts of PeopleSoft – Do's and Don'ts

By Chris Heller • April 15, 2008

Session 3291 in the OAUG section.

I went to Sylvain Nguyen’s presentation on PeopleSoft global rollouts. Sylvain used to be a manager for PeopleSoft Global Financials development, and is now the CEO of Ataway. Ataway is a consulting company that specializes in PeopleSoft (note that we’ve worked with them before)

I came in about 15 minutes late, because the OAUG tracks are not sync-ed up with the Quest tracks timewise. Which is probably driven more by scheduling lunch for everyone here than anything else. Sylvain was in the middle of discussing the question “How can a global rollout be cost efficient, fast paced, and with quality when so many odds are stacked against it?”. This then led into a series of Dos and Don’ts.

Do

  • Define template based global methodology
  • Identify business leaders and analysts in the US and local countries
  • Use local resources in the project team

Don’t

  • Start user requirement gathering before corporate business processes are mapped
  • Underestimate the impacts of working with remote teams

Do you have a US based team travel to each country to gather requirements? Sylvain recommends having local people onsite for the project implementation. They know the business practices, they know the culture, so they can be of great assistance.

Gathering local requirements. When planning deployment, the first thing to do is identify/document your proper business processes. If the core business processes are documented then the requirements gathering is much easier.

One other thing that Sylvain recommended is to avoid consulting companies that don’t have global experience. He gave an example of an implementation where consulting company didn’t know what VAT was, so they left it to be calculated manually. The local users thought this was a joke since the most basic local packages would do this automatically, but they were told that PeopleSoft was state of the art, etc.

The presentation then got into data strategies. It’s common to have a single set of vendors, a small number of setids for US based implementations. That probably won’t work for a global deployment, so if you haven’t looked at how PeopleSoft supports this, then it’s time to learn. (note: a great resource for this is our weblog post on SetIDs and Business Units).

One thing that comes up in some implementations is that the local users already know English, so people wonder why it’s necessary to have the global support. Sylvain gave an example of Japanese users that know English. But they need to interact with other people that don’t. If you send an invoice in English in Japan, you probably won’t get paid because the mailman won’t know how to deliver it. So you do need to be sure that your Japanese users can enter things like addresses in Japanese.

Do

  • Trust psft features around global
  • Prototype early as possible
  • Involve local business leaders in review of designs. Implementation time is too late.

Don’t

  • Underestimate the impact on existing customizations
  • Forget that production support will have to change to handle global users and requirements

There was then a short performance discussion. Most people understand about wide area networking and that there may be performance issues when you have users half way around the world. But you also have to consider things like running batch jobs in the middle of the night in the US. There’s never really a good time to do that in a global implementation because it’s always someone’s work day. So you have to look at better tuning of batch or even locking out users from targeted areas while batch is running.

Global deployments also impact your support organization. If a critical business issue happens at 3am headquarters time, who takes the call? (Hillary!) Would you allow the local teams to make code changes to do a critical fix if needed? Would you make your project team at headquarters wear pagers? Sylvain recommends setting service level agreements up front for these sorts of things so that it can be decided upon rationally up front, instead of waiting for a crisis to happen.

Do

  • Identify and train local SME as early as possible
  • Assign dedicated local support analysts
  • Train the support team on the new processes and features

Don’t

  • Underestimate time and cultural differences in resolving problems.
  • Think the project is over when the country is live.

Sylvain gave an example of a project review where there were no complaints about the new country rollout in Japan. As it turns out, the users were unhappy, but did not want to say anything. This is just a cultural difference, but the project team was not aware that no complaints was not the same thing as no issues.

One question came up at the end was about whether or not to use a single PeopleSoft instance or multiple PeopleSoft instances for development when you have different development teams around the world. Sylvain recommends a single instance so that you don’t have to worry about missing changes from one environement. There were a few nodding heads in the audience that Sylvain pointed out.

Labels: 2008, Collaborate, Global

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Firewall Product as Savior

By Chris Heller • April 11, 2008

We had an interesting situation with one of our customers recently where creative use of one of our products, the ERP Firewall for PeopleSoft, saved the customer from having to do an emergency PeopleTools upgrade. Needless to say, the drinks are on them at Collaborate.

For those that aren’t familiar with our ERP Firewall for PeopleSoft product, it is a Web Application Firewall that has deep knowledge of PeopleSoft applications. It doesn’t just requests coming in as URL strings that someone can write regular expressions to process, it sees the request in the context of PeopleSoft. It knows what a PeopleSoft component is; it knows what a Web Profile is, it understands PeopleSoft security, etc.

The problem that our customer hit was that when someone enters an invalid password logging in to PeopleSoft, PeopleTools would drop the portal and node name from the URL. Normally this wouldn’t be a problem because most people are accessing the default portal in PeopleSoft (generally the EMPLOYEE portal). When you login to PeopleSoft and don’t specify a portal, you get the default portal. Makes perfect sense.

However, when you also have a large number of customers accessing the CUSTOMER portal, then it gets more interesting. The customer end user attempts to login at https://some.host.com/psp/ps/CUSTOMER/CUST/h/?cmd=login . They enter a bad password by accident, and then they get redirected to https://some.host.com/psp/ps/?cmd=login along with the standard message saying that the username or password is incorrect.

So they type in the correct password and get logged in. Except now they are pointed to the EMPLOYEE portal (because the CUSTOMER portal reference got removed). And not being an EMPLOYEE, they don’t have access to anything. Oops. Their session is valid, but the URL is pointing to somewhere where they get nothing.

Turns out that this is fixed in a PeopleTools patch (8.48.13 for the 8.48 codeline, I’m not sure about other PeopleTools versions), but the customer was live with an earlier patch release in the 8.48 codeline and was concerned about dropping a new version of PeopleTools in.

Since they have the ERP Firewall product already (they use it for restricting employees from using the customer facing / internet accessible web server and force them to go through web servers that are just for employee use) we decided to treat accessing the EMPLOYEE portal as a security condition that we want to detect. However, instead of doing something like blocking access, we calculate the proper CUSTOMER portal URL and silently redirect the user there. So we’re actually using a security tool to solve a usability problem.

You might think that just replacing EMPLOYEE with CUSTOMER in the URL would be enough to solve the problem, but there were a few wrinkles which ended up making the ERP Firewall piece a really good fit.

Part of the challenge was making sure that we kept all of the users correct context when redirecting. Most users would be coming through the portal home page, but some might be coming in from deep links into order history or from bookmarks, etc. So we couldn’t just have a single URL to redirect people to.

The stickier problem was that the ERP Firewall needed to redirect differently based on whether the person was logged on or not. If the user was not logged in, and we redirected them to the CUSTOMER portal home page, PeopleTools viewed that as a login attempt, and gave the user the signon page. Normally PeopleTools handles this quite well; an attempt to hit a deep link in PeopleSoft when you’re not logged in gets you the signon page, and once you login, you go to that deep link that you originally requested.

However, due to this bug, the CUSTOMER portal was getting dropped again, so it was necessary to append the cmd=login parameter that PeopleTools recognizes as a request for the login page. Of course, if the user is logged in already and you redirect them with a cmd=login link, then you just killed their session.

The nice thing is that the ERP Firewall for PeopleSoft has the deep knowledge of PeopleSoft to make this trivial. It knows what a PeopleSoft portal is, it knows what PeopleSoft roles a user has, it knows whether they are logged in or not, and it knows how to properly generate and/or modify PeopleSoft URLs in a safe fashion.

If you are interested in learning more about this product, here’s a link to the product page. In addition to providing an overview, you can watch an in-depth demo that shows exactly how it works and how you would configure it.

Labels: , ,

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Reporting against multiple Setids in PeopleSoft

By Chris Heller • April 9, 2008

This is another of those posts that I had intended to get completed a while ago, but ended up getting distracted.

One of our loyal blog readers is at an organization with 42 setids and they are trying to do consolidated reporting across them. This is an issue that I’m very familiar with. Many years ago (12 to be exact), I was doing consulting work at Norrell Services, a staffing company based in Atlanta. They also had a large number of business units and setids and needed to perform reporting across them.

How did you accomplish this?

This was done through a technique I call the Super-Setid. This is where you create a completely new setid that contains the union of all values across the different setids. You can then build trees and do other things against that new setid. When doing nVision reporting, you can create a Super Business Unit that is intended to map to the super-setids of your chartfields (and pull in your trees as well). Because the report request in nVision can have a business unit different than the data you’re reporting against, this is possible (which also means that your business unit will be used for setid mapping only). For those who kept track of features in Financials at PeopleSoft, they had tried to accompplish this with a feature called Partial Tableset Sharing, which was targeted to Financials 7.5, but was pulled at the last minute and never released because they were never able to get it to work properly across all the different use cases customers needed.

What’s involved in implementing the Super Setid?

Good question, as always. To implement this, you will want to create a surrogate table for the valid values of each field you want to use a super-set for. This table will be used as the foundation for your trees, for filtering data in your reports, and for getting additional attributes.

To create the surrogate table, there are two main options:

  • Create a view against your valid value table
  • Create a new table and populate it manually
  • Use PeopleSoft’s summary ledger and summary tree functionality

View

Let’s start by looking at the view. In order for the view to work, your any chartfield or other table you’re creating the superset for must have values that are either unique or consistent across the different setids. In other words, you cannot have department 1000 mean Finance in one set id and have department 1000 mean Manufacturing in another setid. This is usually the case, but in the scenario where an organization does a quick migration of an acquisition into PeopleSoft and they don’t convert chartfield or other values during the migration, this may not be the case.

Let’s use the DEPT_TBL as an example for creating the view. The first step is to open up the record definition for DEPT_TBL, save it under a new name, and remove the fields you don’t plan to use for reporting. Then, you change it to be a view and enter the select criteria for the view (hard-coding the setid to be your super-setid). Here is an example of this select statement:

SELECT 'CONS'
, A.DEPTID
, A.EFFDT
, A.EFF_STATUS
, A.DESCR
, A.DESCRSHORT
, A.COMPANY
, A.SETID_LOCATION
, A.LOCATION
, A.TAX_LOCATION_CD
, A.MANAGER_ID
, A.MANAGER_POSN
, A.BUDGET_YR_END_DT
, A.BUDGET_LVL
, A.GL_EXPENSE
, A.SYNCID
, A.SYNCDTTM
FROM PS_DEPT_TBL A
WHERE A.SETID = (
SELECT MAX(B.SETID)
FROM PS_DEPT_TBL B
WHERE B.DEPTID = A.DEPTID)

Populating a new table

Let’s move on to discuss how to populate a separate table with data. This gives you a bit more control over the process of doing this than a view would provide (such as rules for picking values that may be duplicated across setids). There are two ways of accomplishing this: the first is to write an application engine program you run periodically to move the data (and the SQL statement above could be the starting point for doing %InsertSelect). Another option is to add SavePostChg Peoplecode to the record you’re doing this for.

Let’s focus on DEPT_TBL as an example for this again. I’ve created a new record definition called DEPT_CONS_TBL, which has a subset of the fields in the DEPT table I want to use for reporting. I, then added SavePostChange peoplecode to the SETID field on the DEPT_TBL record. This means that every time data is saved into the table, my PeopleCode will be invoked to update my new table.

/* Grey Sparling Solutions - Create SuperSetid value */
/* */

rem Check to see if current value currently exists in Super Setid Record;
Local Record &SuperSetidRec;
Local Record &CurrentRec;
Local string &SuperSetidVal = "CONS";

&CurrentRec = GetRecord(Record.DEPT_TBL);

&SuperSetidRec = CreateRecord(Record.DEPT_CONS_TBL);
&SuperSetidRec.GetField(Field.SETID).Value = &SuperSetidVal;
&SuperSetidRec.GetField(Field.DEPTID).Value = DEPT_TBL.DEPTID;


If &SuperSetidRec.SelectByKeyEffDt(DEPT_TBL.EFFDT) Then

rem Update existing value;
&CurrentRec.CopyChangedFieldsTo(&SuperSetidRec);
&SuperSetidRec.Update();


Else

    • rem insert new value;

 

    • &SuperSetidRec = CreateRecord(Record.DEPT_CONS_TBL);

 

    • &CurrentRec.CopyFieldsTo(&SuperSetidRec);

 

    • &SuperSetidRec.GetField(Field.SETID).Value = &SuperSetidVal;

 

    • &SuperSetidRec.GetField(Field.DEPTID).Value = DEPT_TBL.DEPTID;

 

    &SaveRtn = &SuperSetidRec.Save();

End-If;

Summary Ledger and Summary Tree

The last option we’ll discuss is using summary ledgers and summary trees for accomplishing this. This option requires using a little bit of abstract thinking. Imagine that you are capturing data at a very granular level (i.e. low-level accounts), but you don’t need to perform consolidated reporting against that level. In other words, you can show your values aggregated a bit when doing reporting. If this occurs, then you don’t need to have a single set of valid values at the detail level that works across setids. Instead, you can get a consistent picture by ensuring that your trees have a consistent node structure across your different setids and use those nodes for reporting. You will, in essence, set up your trees to have a common hierarchy, but the sets of values linked in at the leaf level will be different for each setid. The tree will actually be doing your mapping for you.

You will then run PeopleSoft’s summary ledger build process for each business unit, where you aggregate your chartfields to a specific level in these trees (thus storing the values with a consistent set of chartfield values across setids). Viola!

Putting this to use

Okay. Now you’ve handled the data manipulation to get a consistent set of values across your setids. Now, you need to use this in your reporting. How do you do it?

The first thing to do is to build a tree that you can use. If you’re creating the super-setid with a view or table, then you will create a tree structure that uses your new table for the leafs, and build a tree with that structure using your super-setid. You will also want to use that table for valid values in reporting (for value criteria in nVision or for query). If you use the summary ledger option, you will build your report against the summary ledger and report across business units. You would also build a summmary tree as referenced in PeopleBooks.

Code

Here is a project with the code referenced above.

Labels: nVision, Reporting

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Links not Automatically Opening a Page

By Chris Heller • March 17, 2008

This is another blog posting that Chris and I had discussed putting out there (but got distracted doing other things, one of which is keeping up with the PeopleSoft community… Chris calls this Yak Shaving.)

So I finally found this posting on ITTOOLBOX, and realized that it was time to discuss some of the intricacies of bypassing the search page here.

For those who have been following the blog, you may be familiar with some of the posts we’ve done on drilling and PeopleSoft.

  • Adding hyperlinks to Queries and nVision reports (described in this powerpoint).
  • Drilling to other Content in nVision blog posting.
  • Drilling Deeper into PeopleSoft Pages blog entry.
  • Code to drill to any chartfield blog entry.
  • Drilling to a performance or development document from a URL blog posting.

So, as you can see, drilling is a recurring theme here in the blog. Let’s look at what the component processor does, and how this affects drilling.

How the component processor handles drilling

Let’s start by revisiting the how you can create a URL that opens up a specific page with a specific item. The first thing is to look at the structure of the PeopleSoft URL to do this.

http://www.psserver.com/ Server
psp Portal or Content
psp=Show portal frame
psc=Show content only
/ps/
EMPLOYEE Portal Name
/ERP/ Site
c/
PROCESS_JOURNALS PeopleSoft Menu
.JOURNAL_ENTRY_IE.GBL Component and Market
?
BUSINESS_UNIT=US005& Search Parameter
JOURNAL_DATE=2004-03-31& Search Parameter
JOURNAL_ID=MKMAR5& Search Parameter
ACTION=U& Mode
A=Add
U=Update
C=Correction
PAGE=JOURNAL_ENTRY1 Page Name

So, from the above table, you can see that many of the PeopleSoft artifacts have a place in the URL. The menu and component tell you what component to invoke (and notice that the page within the component is a parameter just as is the mode in which the component is brought up). Any field on the search record of the component can be passed as a parameter, but you need to have all of the (primary) Search Keys passed if you want to bypass the search page.

Okay, I think I get it, but what can cause it to trip up?

Well, there are 3 major things that will bring you back to the search page versus getting directly into the component.

  • Not all primary search fields are passed in the URL
  • The component is set to force the search page to display
  • The component processor encounters an unexpected condition.

Let’s go through each of these one at a time

Not all primary search fields are Passed

This was actually the problem with opening up the Job page. You see, the EMPL_RCD field is a primary search field for the Job component. This is because employees could potentially be in more than one job (although in my experience it doesn’t happen that often). In this example, just adding &EMPL_RCD=0 to the URL will do the trick if your employees only have 1 job. Although you could create a new menu item that overrides the search record for certain situations, it wouldn’t work here because the key structure of the level 0 record of the component expects EMPL_RCD key to be passed in.

One other thing to note, is that quite often it is desirable to invoke the search page with a subset of parameters populated (to target the search results). One example that we show is drilling into tree manager, populating the fieldname, so that you can see all trees built against that field. We use this to facilitate the configuration of our report explorer product, where we know what field the user is interested in, but not which tree they would want to use.

The Component is set to force the search page to display

So, you may ask yourself why anybody would design a page so that you have to display the search page. The answer is that you can use PeopleCode to implement row level security in the SearchInit and SearchSave events (which has been done by PeopleSoft developers in spite of the fact that it isn’t recommended). The only time SearchInit or SearchSave events fire is when the search page is displayed, which means that for those components our little trick of passing in values would actually bypass the security implemented for that page. This is discussed in quite a bit more detail here.

The component processor encounters an unexpected condition

So, let’s say that you have a URL that opens up a component in add mode, and there is already a row in the search table that contains all of the keys passed in. Or, let’s say that you pass in a set of search keys to open the page in update mode, but there isn’t any data in the search record table. In both of these circumstances, the component processor will determine that it can’t meet the request and will display the search page to allow the user to correct the problem (i.e. change the values passed in or change the mode in which the page will be invoked).

Labels: Drilling

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Can you use Winter or Node-Oriented Trees in GL?

By Chris Heller • March 14, 2008

This is a question that came up on the ITToolbox forum peopletools-l. I decided that it would be a good idea to flush out my answer in additional detail (and post it here). Interestingly, as I did my research, I discovered that how commitment control uses trees was more complex than I thought. I also discovered that one of my statements in my response was inaccurate.

Here is a link to the post.

Can you do it?

The answer to whether you can use them in GL is still yes, and the method by which you use them is the same as how you would use them elsewhere. For those who don’t want to read all the way through my blog post that discusses trees, here is some background on winter and summer trees.

Winter and Summer Trees

The naming of summer and winter trees comes from whether they have leaves on them or not. So, just as with a deciduous tree, a summer tree has leaves and a winter tree doesn’t.

The first question that may come to mind is “why have leaves in one type of tree, but not another?” The answer to that question is whether the hierarchy is logically separate from the items being organizaed by the tree. For example, in a position hierarchy, each position belongs at a specific place in the hierarchy, so there are no need for leaves. By contrast, in most department hierarchies, departments are rolled up into offices, regions, divisions, etc., which means that departments are the leaf, and the rollups are not actually departments (and are the nodes).

When these trees are used by products, such as nVision and Query, those tools understand how to gather the underlying information for subtotalling and filtering based on the tree type (the join for a winter tree uses the nodes for joining directly to the data table, whereas the join for a summer tree uses the nodes to identify the leaves to join to the data table).

Why would you want to use Winter Trees in GL?

This is a good question, because at first blush, most reporting is done against aggregations of chartfields in GL. However, there is one very good scenario where you may want to eliminate the leaves and have each node in the tree be a chartfield value in your ledger table: budgeting.

When you do budgeting, quite often it is just too cumbersome to budget everything at the lowest level of granularity in which you capture transaction data (i.e. your journals). If you make the decision that you will create summary accounts that represent different rollups in your hierarchy, then a winter tree will work for you.

The delivered PeopleSoft demo database has an example of a winter tree on accounts (and interestingly enough, it’s called “CONTROL_BD_ACCOUNTS”.

One thing to keep in mind, however, when having aggregate chartfields values that you use in your trees, is that they are real chartfield values that could have values posted to them unless you do something to specifically prevent that. The Financials developers have done exactly that for the budgeting scenario. When you set up a GL account, you can specify it as being a Budgetary Only account, which will allow you to post data into budget ledgers, but not actual ledgers for that account.

Spring Trees

I think it’s also important to mention Spring Trees, because they are similar to winter trees in several ways (and this is something I got wrong in my answer on ITTOOLBOX). Strictly speaking, though, Spring Trees are summer trees.

This is because Spring Trees do have both leaves and nodes. However, in a Spring Tree, the Leaf and the Node attributes are stored in the same table and use the same field.

This is a relatively curious, because a winter tree would accomplish exactly the same thing, especially since the aggregate accounts (i.e. the ones referenced on nodes) are not duplicated on the leaves. I think the main reason for this is that the tree nodes themselves drive the commitment control rules for determining whether you have budget or not, whereas the leaves are the level at which the data is posted. However, it looks like I need to do a bit more research on this.

Labels: Tree_Manager

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Baby steps with Reporting against PeopleSoft

By Chris Heller • March 13, 2008

So, you know that there’s opportunities for improving your end-users’ ability to get meaningful information out of PeopleSoft, but it seems very daunting. We’ve had several discussions with organizations in the past 2 weeks, where they see the opportunity, but don’t know where to get started.

This is a bit of a different spin on the following blog entry.

Baby Step 1 – Ad-hoc Queries
The first step is to start leveraging PS/Query as a means for getting data out. Many organizations have already been taking advantage of PS/Query (I know of several situations where organizations have tens of thousands of queries).

If you’re not using Query, then I suggest your first step to be to centrally develop a small number of queries to provide answers to common questions and then secure them with object security to be read-only. PeopleTools 8.44 added the ability to run a read-only query, but not save it (which means your end-users can use them without being able to change or break them).

When you create these queries, you will want to try to make them as useful as possible to a wide range of users. This generally means:

  • You will want to use prompts in the queries to allow end-users to use the same query to answer multiple questions. One neat trick on the prompt side is to put wild-card criteria in it (in other words, make it so that the criteria does something like this: WHERE ….. AND ( A.DEPTID = :1 OR ‘*’ = :1)
  • You will want the query to include fields to answer as many questions as possible related to the prompts and data source as possible.

Baby Step 2 – Formatting the Queries

Now that you’re using PS/Query, the first thing your users will want to do is to change them, either by re-sorting the data, by applying additional filters, or merely by making the output look better.

Many of your more savvy users will run the Query to Excel and do this themselves. The first thing they will do is put an auto-filter on the data, so that they can use the query results as a data source for doing additional querying (using the auto-filter). They may also resize columns and apply other formatting to the data.

Unfortunately, this can be very cumbersome to do manually every time your end-users run a query. Therefore, they will very quickly ask whether there is a way to automate this. There are two options available to you:

  • A tabular nVision report with an InstanceHook macro in it to format the data. This option was discussed in the following posting. The nice thing about a tabular nVision report is that it’s essentially a Query that is specifically designed and formatted, so it’s relatively simple to do.
  • Our Excel Add-in product, which will take the results of any query and automatically format and add auto-filters to it. The nice thing about this approach is that any query your end-users run will automatically be usable in this way (whereas somebody has to build an nVision report and write the macro code to apply auto-filters in nVision).

Baby Step 3 – Linking Queries

As your users start getting used to running queries and formatting them, they’ll start wanting to include more and more information in the query results. This is a natural part of the process, because once they learn one thing, users will want to see information related to it.

When your organization reaches this milestone, you will want to be very careful. Many organizations start extending the queries by joining related data into their queries. This can lead to “Kitchen Sink” queries and can cause two issues:

  • The query results start getting too large to understand. Now, the users have to start wading through the columns to find the data they’re interested in.
  • The queries become difficult to develop and maintain. This is because as you join in disparate data sources, the SQL gets more complex, and you often start introducing cartesian products in your results that you have to find and troubleshoot.
  • Performance issues start to crop up. As the SQL gets more complex, the database has to do more work.

The best approach for this is to find ways to link queries together. Often, this is called drilling. There are 3 main ways of linking your queries together:

  • Modify the query to have hyperlinks in the result set. This is covered in the powerpoint and code attached following blog entry. This means you will have to pre-think and code this into the query.
  • Believe it or not, our Excel Add-in product will allow you to drill from any Query you’re viewing in Excel, allowing you to link one query to another without having to write code or dirty up the output with hard-coded links to other queries.
  • Utilize drilling in nVision. I will discuss this further in the next topic, because the core functionality of drilling in nVision is not exposed for query-style output (so we haven’t yet taken that baby-step in this blog entry).

Baby Step 4 – Aggregating information

Okay, now your end-users are getting information and drilling into related information. However, now they want to do some comparisons. How are they doing related to their budget? Has the average customer satisfaction been going up or down over time? Instead of seeing lists of information, now they want to start aggregating it.

The first and easiest step of this is to simply put subtotals into their queries (which can either be done manually by you or automatically with our Excel Add-in product). This will allow you to see counts and sums of your results broken out for each field you sort on.

The second step is to leverage PivotTables in Excel, which will allow you to do the analysis in a cross-tab format. Again, your users can do this using the Excel menus or you can automate this in a tabular nVision report and and InstanceHook macro (again the example is covered for pivot tables here).

Baby Step 5 – Aggregating and Comparing across different items

Although Subtotals and Pivot Tables on top of queries provides a lot of value to your users, you will quickly reach the point where your users will want more. As we discussed in Baby Step 3, your queries will have a targeted set of data (which means that the data available for your pivot tables is limited). What your users will want to do is to do comparisons between different items (such as comparing customer opportunities with customer satisfaction ratings).

The common thread for doing this type of anlaysis is that there’s a set of “attributes” (often called dimensions) that you want to compare “data” (often called metrics, KPIs, or facts) against. There are a couple of options available to you here.

  • Matrix reports in nVision. nVision allows you to take different queries (or Ledgers) and organize the data by common attributes, such as trees, chartfields, or timespans. Because you already have experience with Queries (and because nVision is included with PeopleSoft), this is often a natural step to take. The following blog entry has examples of nVision matrix reports for different subject areas.
  • A BI tool, such as OBIEE, Hyperion Essbase, Cognos PowerPlay, or Microstrategy. These tools require additional development effort to model the relationships and often to extract the data from PeopleSoft. However, if your organization has already standardized on one of these tools (and especially if you have a Dat a Warehousing group), you may be forced to go in this direction.

Summary

Although there are other aspects we could look at, I believe that this is the best place to start (especially since many other aspects are more difficult to accomplish in baby steps). Another benefit of this approach is that your learnings can be applied to alternative technologies (i.e. even if you end up building a data warehouse, building a set of nVision reports helps you identify the data you want, how to aggregate them, and what facts your users want prior to building your first ETL map).

Labels: excel, nVision, Query, Reporting

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

An open letter to Carl Icahn

By Chris Heller • November 5, 2007

Dear Mr. Icahn,

We see that you are getting access to BEA’s internal financial system. As you may or may not be aware, BEA uses PeopleSoft for their internal operations systems.

To help you analyze the numbers quickly, Grey Sparling Solutions would like to offer you a free copy of our Excel Add-in for PeopleSoft. Our Excel Add-in offers greatly enhanced drilling capabilities over what is generally available from PeopleSoft’s reporting tools, including the ability to drill to the underlying transactional pages.

It will definitely help you and your team quickly evaluate whether BEA is worth more than $17 per share or not. Here is a Flash demo of the product in action. All you need is the URL for the BEA financial system and the login that they are providing you.

As stated above we’re offering this to you free of charge. A nice thank you during the press conference where you announce your findings would be appreciated, but certainly not required.

Sincerely,

Grey Sparling Solutions

Put the Appsian Security Platform to the Test

Schedule Your Demonstration and see how the Appsian Security Platform can be tailored to your organization’s unique objectives

Request a Demo

Start your free demo

"Learn how you can reduce risk with rapid threat protection, audit response and access control. All from a single, comprehensive platform"

Trusted by hundreds of leading brands