Reverse Engineering Mobile Apps for Oracle EBS – part 2

Reverse Engineering Mobile Apps for Oracle EBS – part 2

Gepubliceerd: Categorie: Oracle

In the first part of this article we have shown some of the inner workings of the Timecard mobile app. In this second part we will focus on the REST Login API provided by Oracle EBS.

Just as in the first part we need to have the Timecard MAA file from Oracle opened in JDeveloper, and access to Oracle EBS. For probing Oracle EBS REST services we have used Postman.

Probing the REST Login Services

When developing our own mobile app for Oracle EBS, we will also need to use EBS’ REST login services. To get a better understanding of this API we will do some API probing using Postman. Our goal is to simulate the login for a mobile app using the REST API. Once authenticated, we will try to fetch timecards for a particular user.

Fetching Timecards – take 1

In the decompiled Java sources (mobileApplicationArchive.jar in ViewController\classlib) we can find the REST call going to Oracle EBS in the TimecardWS.java class, located in package oracle.apps.hxc.mobile.ws

Figure 1: TimecardWS.java showing REST call for fetching all timecards
  1. public static void fetchDetails()
  2. {
  3. RestUtil rest = new RestUtil();
  4. rest.setConnection("EBSConn");
  5. rest.setRequestType("POST");
  6. rest.setRetryLimit(0);
  7. rest.setRequestURL("/OA_HTML/RF.jsp?function_id=HXC_MOB_TC_STATUS");
  8. String response = "";
  9. sb.append("<params> <param>ALL</param>");
  10. sb.append("<param>");
  11. sb.append("yyyy-MM-dd");
  12. sb.append("</param></params>");
  13. rest.setRequestXML(sb.toString());
  14. try
  15. {
  16. response = rest.processRequest();
  17. }
  18. }

This call looks rather simple. It concerns a HTTP POST with request URL: http://ebsURL:port//OA_HTML/RF.jsp?function_id=HXC_MOB_TC_STATUS

The POST payload should be the following XML:

  1. <params>
  2.     <param>ALL</param>
  3.     <param>yyyy-MM-dd</param>
  4. </params>
Figure 2: Postman invoking REST call on Oracle EBS. Please mind setting the content-type to application/xml

When trying to invoke this method in Postman (figure 2) we get the following error response:

  1. <response>
  2.  
  3.     <status>
  4.  
  5.         <code>401</code>
  6.  
  7.         <description>FND_ERR_DOLOGIN</description>
  8.  
  9.     </status>
  10.  
  11.     <data></data>
  12.  
  13. </response>

This seems like a logical response because we did not provide any credentials yet.

For doing a REST call the Timecard app uses the RESTUtil class from package oracle.apps.hxc.application.util. When investigating this utility class however, we did not find any login wrapper. So where to find the login procedure?

Fetching Timecards – take 2 (Login API)

The login code is hidden somewhere in the ApplicationController project. In this project you will find the EBSLoginLib.jar library containing login related logic. To investigate the implementation we first need to unzip the jar file, and decompile all Java classes using jad.

  1. unzip: Timecards_Archive\lib\EBSLoginLib.jar
  2. Recursive Jad: jad -d . -s java -r **/*.class

The class to look for is the RestUtil from package oracle.apps.fnd.mobile.common.utils. This is a different RestUtil than the one from the Timecard ViewController project. What we found here was a REST call to some sort of login function.

Figure 3: Java code executing some sort of Login on EBS using REST GET request
  1. restServiceAdapter = Model.createRestServiceAdapter();
  2. restServiceAdapter.clearRequestProperties();
  3. restServiceAdapter.setConnectionName("EBSRestConn");
  4. restServiceAdapter.setRequestType("GET");
  5. StringBuffer sbUrl = new StringBuffer("");
  6. sbUrl.append("/OA_HTML/RF.jsp?function_id=mLogin");
  7. restServiceAdapter.setRequestURI(sbUrl.toString());
  8. restServiceAdapter.addRequestProperty("Content-Type", "application/xml");
  9. String response = restServiceAdapter.send("");

We simulate this GET request to endpoint http://ebsurl:port/OA_HTML/RF.jsp?function_id=mLogin in Postman (see figure 4).

Figure 4: Postman login request without providing credentials

Without providing any credentials we get an ‘Invalid username/password’ error from the Login service. Seems logical to us, so we try again using some Basic Authentication. For this we enter our Oracle EBS user credentials.

Figure 5: Postman succesful login request when providing EBS credentials

The response contains an access token which is valid for a certain amount of time.

  1. <response>
  2. <data>
  3. <accessToken>GhuAjvARQpie3uh6Q0Tl5o6kti</accessToken>
  4. <accessTokenName>VIS</accessTokenName>
  5. <ebsVersion>12.1.3</ebsVersion>
  6. <userName>BPALMER</userName>
  7. </data>
  8. </response>

It seems we have logged in. Let’s try to get the Timecards again.

Figure 6: Fetching Timecards take 2 fails. No responsibility selected yet

Still no timecards in the response! But we are one step further in the login procedure. We still need to initialize our session by choosing a responsibility.

Fetching Timecards – take 3 (Responsibility Selection)

For REST API calls concerning Responsibility selection we check RespUtils.java located in package oracle.apps.fnd.mobile.common.utils.

Here the initialization of the session is implemented in method mInit.

Figure 7: Java code calling an initialization REST API for EBS
  1. private static String mInit(String respId, String applId, String orgId, String securityGroupId)
  2. throws Exception
  3. {
  4. String response = "";
  5. orgId = orgId != null && !orgId.isEmpty() ? orgId : "";
  6. RestUtil rest = new RestUtil();
  7. rest.setConnection("EBSRestConn");
  8. rest.setRequestURL("/OA_HTML/RF.jsp?function_id=mInit");
  9. String input = (new StringBuilder()).append("<data> <resp> <id>").append(respId).append("</id>").append(" <key></key>").append(" <applId>").append(applId).append("</applId>").append(" <applKey></applKey>").append(" </resp>").append(" <securityGroup>").append(" <id></id>").append(" <key>").append(securityGroupId).append("</key>").append(" </securityGroup>").append(" <org>").append(" <id>").append(orgId).append("</id>").append(" <key></key>").append(" </org>").append("</data>").toString();
  10. rest.setRequestXML(input);
  11. try
  12. {
  13. response = rest.processRequest();
  14. } catch(Exception e) {
  15. throw e;
  16. }
  17. return response;
  18. }

The REST endpoint used is: http://ebsurl:port/OA_HTML/RF.jsp?function_id=mInit

The POST message being sent looks like the one below:

  1. <data>
  2. <resp>
  3. <id>$RESPID</id>
  4. <key></key>
  5. <applId>$APPLID</applId>
  6. <applKey></applKey>
  7. </resp>
  8. <securityGroup>
  9. <id></id>
  10. <key>$SECURITYGROUPID</key>
  11. </securityGroup>
  12. <org>
  13. <id>$ORGID</id>
  14. <key></key>
  15. </org>
  16. </data>

Now we still have to fill in the blanks! Which id's to use? For this we move to the Responsibility.java in package oracle.apps.fnd.mobile.common.resppicker. The method of interest is getResponsibility() which will fetch all relevant responsibilities for the logged in user and pass it to the Responsibility Picker component.

Figure 8: Java code for getting EBS Responsibilities which can be used for this mobile app
  1. rest = new RestUtil();
  2. rest.setConnection("EBSRestConn");
  3. rest.setRequestURL("/OA_HTML/RF.jsp?function_id=mAcs");
  4. JSONObject json = new JSONObject();
  5. String response = "";
  6. Map propsMap = PropertiesReader.getPropsMap();
  7. String appName = (String)propsMap.get("oracle.ebs.login.roleappname");
  8. String mode = (String)propsMap.get("oracle.ebs.login.rolemode");
  9. String roleCode = (String)propsMap.get("oracle.ebs.login.rolecode");
  10. try
  11. {
  12. json.put("appName", appName);
  13. json.put("mode", mode);
  14. json.put("roleCode", roleCode);
  15. }
  16. catch(JSONException e)
  17. {
  18. e.printStackTrace();
  19. }
  20. rest.setRequestXML(json.toString());
  21. customHeaders.put("Content-Type", "application/json");
  22. customHeaders.put("Accept", "application/json");
  23. String response = rest.processRequestWithCustomHeaders(customHeaders);

As you see the REST endpoint is http://ebsurl:port/OA_HTML/RF.jsp?function_id=mAcs the payload is formatted as a JSON string, and there are 3 JSON parameters part of the request.

We have looked at the properties, such as oracle.ebs.login.roleappname in the ebs.properties file. Using these properties we came up with the following JSON payload:

{
  "appName": "HXC",
  "mode": "parent",
  "roleCode": "UMX|HXC_MBL_TIME_ENTRY"
}

We’ve then executed this call using Postman (see figure 10 and 11).

Figure 9: Use Postman to fetch responsibility details needed for finally initializing our login
Figure 10: Be aware to select ‘raw’ data as the request and select content type JSON

The response (figure 11) contained the data we needed for our Responsibility initialization. Such as the responsibility_id, application_id, and security_group_key.

Figure 11: JSON response when trying to fetch Time Entry responsibility details

{
  "data": [
    {
      "name": "FND_RESP|HXC|HXC_SELF_SERVICE|STANDARD",
      "display_name": "Self Service Time and Expenses",
      "responsibility_id": "22717",
      "responsibility_application_id": "809",
      "security_group_key": "STANDARD"
    }
  ]
}

Finally we can initialize the responsibility. We have used the responsibility_id (22717), the responsibility_application_id (809) and the security_group_key (STANDARD) for our session initialization request (see figures 12 and 13).

Figure 12: Request for initializing the session
  1. <data>
  2. <resp>
  3. <id>22717</id>
  4. <key></key>
  5. <applId>809</applId>
  6. <applKey></applKey>
  7. </resp>
  8. <securityGroup>
  9. <id></id>
  10. <key>STANDARD</key>
  11. </securityGroup>
  12. <org>
  13. <id></id>
  14. <key></key>
  15. </org>
  16. </data>
Figure 13: Postman initializing session with responsibility for accessing mobile timecards

In our final take we try to fetch all Timecards for this user using the http://ebsurl:port/OA_HTML/RF.jsp?function_id=HXC_MOB_TC_STATUS endpoint (see figures 14 and 15).

Figure 14: Postman logged in and initialized finally fetching the list of Timecards using the EBS REST API
Figure 15: XML Response with the Timecard data. We see two timecards have been fetched
  1. <?xml version = '1.0' encoding = 'UTF-8'?>
  2. <response status="200">
  3. <timecard_list>
  4. <timecard>
  5. <tc_start_time>2016-04-03</tc_start_time>
  6. <tc_id>219199</tc_id>
  7. <tc_ovn>2</tc_ovn>
  8. <layout_id>4197</layout_id>
  9. <line1>2016-04-03 to 2016-04-09</line1>
  10. <line2>40 Hours</line2>
  11. <tc_status>SUBMITTED</tc_status>
  12. <month>PREV</month>
  13. </timecard>
  14. <timecard>
  15. <tc_start_time>2016-04-10</tc_start_time>
  16. <tc_id>219226</tc_id>
  17. <tc_ovn>2</tc_ovn>
  18. <layout_id>4197</layout_id>
  19. <line1>2016-04-10 to 2016-04-16</line1>
  20. <line2>40 Hours</line2>
  21. <tc_status>SUBMITTED</tc_status>
  22. <month>PREV</month>
  23. </timecard>
  24. </timecard_list>
  25. </response>

Conclusion

It took some Java decompiling and reverse engineering, but finally we were able to simulate the E-Business Suite login using the Authentication REST APIs.

Please keep in mind, when creating your own mobile app for Oracle EBS, you do not need to program all these REST calls yourselves. The EBSLoginLib.jar in combination with the Sample App from Oracle can take these worries away for you. This is described in detail in the Developer’s Guide [1].

It is also possible to just enable your own API’s in the Integrated SOA Gateway, and forget about the entire session context. Just use basic authentication on each REST call to access the services. But bear in mind that using basic authentication alone won’t initialize a session context. So there will be no responsibility or organization context, unless you explicitly code this into your API.

References

[1] Oracle® E-Business Suite Mobile Foundation Developer's Guide

[2] Timecards for EBS

[3] Introduction to Oracle Mobile Application Framework

Richard Velden
Over auteur Richard Velden

Oracle Fusion Middleware Developer at Qualogy. Specializes in integration and cloud development using Oracle technologies such as: SOA Suite, Service Bus, Integration and Process Cloud.

Meer posts van Richard Velden
Reacties (1)
  1. om 05:05

    Question: How can <ebsurl>/OA_HTML/RF.jsp?function_id=mLogin be made to accept SSO (OAM) credentials. It seems that <ebsurl>/OA_HTML/RF.jsp?function_id=mLogin only authenticates with Local EBS credentials or if integrated with OID - OID credentials (with some settings for EBS). We have EBS 12.2 integrated with OAM and wanted to use OAM credentials to authenticate so that more elaborate authentication mechanism can be leveraged.

Reactie plaatsen