Originally updated on September 12, 2018 by Murali k |
With Manage Layout being removed as part of SI 6.0, no custom tabs can be introduced and customers will have to migrate their Portlet based web components with integrating custom UIs into Dashboard. Since Menu item on the Dashboard left-nav-bar is configurable, one can configure a new URL that points to a webapp that's hosted on HttpServerAdapter or on an external web server.When the new URL is clicked, the webapp can do the SSO with B2Bi and open the url in a new Window/Tab
Steps to add custom link in SI dashboard Menu
- System User can create an installation jar which will get installed to SI using InstallServices.sh or InstallServices.cmd.
- The installation jar should have following files laid out properly. It must follow the directory structure
< Project_name>
----- exploded_wars
-----------------------admin
------------------------------------jsp
-----------------------------------------custom.jsp
----- factorysetup
-------------- XMLS
----------------------- YFS_MENU.xml
----------------------- YFS_RESOURCE.xml
-------------- installer.xml
-------------- custom_menu_installer.xml
------files
-------------------properties
-------------------------------lang
---------------------------------------en
------------------------------------------------------ Sitemap_en.properties_custom_ext
- Create custom jsp and place under Project_name/exploded_wars/admin/jsp/custom_folder_name/custom.jsp
Actual content will go here in this jsp (user can give any name for this jsp). This JSP will have an link to launch custom application.
- Add entry at Project_name/factorysetup/XMLS/ YFS_MENU.xml to create custom menu items. It will appear on left tree menu on dashboard. User can provide sequence for reordering.
Example is as below –
<YFS_MenuList>
<Menu DisplayMode="" Icon=""
MenuDescription="CustomMenu"
MenuKey="ADM_62_010" MenuSeq="62" MenuType="MENU"
ParentMenuKey="ADM" ResourceKey="" SystemDefined="Y"/>
<Menu DisplayMode="" Icon=""
MenuDescription="CustomLink"
MenuKey="ADM_62_010_010" MenuSeq="10" MenuType="MENU"
ParentMenuKey="ADM_62_010" ResourceKey="NEWCUSTOMLINK" SystemDefined="Y"/>
</YFS_MenuList>
- Create resource entries for the page defined at below location
Project_name/factorysetup/XMLS/YFS_RESOURCE.xml
Example is as below –
<?xml version="1.0" encoding="UTF-8"?>
<YFS_ResourceList>
<Resource ApplicationCode="plt" ApplicationName="PLTADM0001"
CanAddToMenu="Y" DocumentType="" IsPermissionControlled="Y"
ParentResourceId="PLTADM0001" ResourceCreateType="SYSTEM"
ResourceDesc="custom sample link" ResourceId="NEWCUSTOMLINK"
ResourceKey="NEWCUSTOMLINK" ResourceSeq="210" ResourceType="0"
SupportsSearchToDetail="N" Url="./Page?next=page.samplecustomappmgmt"/>
</YFS_ResourceList>
- Make jsp entries in page.properties.in file
For example page.<Name of page>.path = /jsp/custom_folder_name/custom.jsp
- Add entries for the custom menu description defined in YFS_MENU.xml as above in the file
Sitemap_en.properties_custom_ext.
For example -
Link.CustomMenu = Custom Menu
Link.CustomLink = Open Custom Web App
- Create installer.xml and custom_menu_installer.xml and mention the resource xml file which need to be installed.
In custom_menu_installer.xml we need to mention the name of xml files created under XMLS
Sample Entry for custom_menu_installer.xml
<Task Class="XMLMigrator" Description="custom menu Installation" When="First">
<TaskInfo Completed="N">
<EntityList>
<Entity AbortOnError="N" Class="YFS_Resource" CommitCount="50" Completed="N" NumRecordsProcessed="0" ResetEntityAttributes="Y" TruncateTable="N" XMLFile="YFS_RESOURCE.xml"/>
<Entity AbortOnError="N" Class="YFS_Menu" CommitCount="50" Completed="N" NumRecordsProcessed="0" ResetEntityAttributes="Y" TruncateTable="N" XMLFile="YFS_MENU.xml"/>
</EntityList>
</TaskInfo>
</Task>
Sample Entry for installer.xml
<Task When="AfterChildren" Description="Custom Menu Installation" Class="DoNothingMigrator">
<TaskInfo Completed="N"/>
<ChildTasks>
<Task When="First" Description="Custom Menu installation" Class="MigratorMain">
<TaskInfo Completed="N" InputFile="custom_menu_installer.xml"/>
</Task>
</ChildTasks>
</Task>
Here we need to mention custom menu installation.xml
- Create jar file with the same folder structure as specified in above step
- Go to <SI install Location>/bin. and run ./InstallService.sh <jar file Name>
- Restart SI server.
Once we have done this steps we should see custom menu links in SI dashboard.
SSO to custom Web App
The REST API’s at B2Bi end used for SSO and authentication will be deployed as a WAR file on a HTTP Server adapter in B2Bi. Below is the HTTP Server Adapter deployed with rest war over SSL.
We have custom jsp (gets served when clicked on custom link created above) which would provide an link to launch an custom web app running on different server.
Sample custom jsp file which have link to launch the custom web application is as below,
Below is the custom jsp file code logic to fetch the dlsso token, username and nodename which is used by custom web app to authenticate with SI when did an SSO to custom web application by clicking on the custom link.
<%@page language="java" import="java.net.URLEncoder,com.sterlingcommerce.woodstock.ui.UIGlobals,com.sterlingcommerce.woodstock.ui.dlsso.DLSSOSessionHash,java.net.InetAddress,java.net.UnknownHostException,com.sterlingcommerce.woodstock.util.frame.Manager" errorPage="/jsp/error_report_info.jsp"%>
<%
String userName = (String)session.getAttribute(DLSSOSessionHash.USERNAME_ATTR);
String dlssoToken = null;
Cookie [] clist = request.getCookies();
for(int i = 0 ; i < clist.length ; i++ ) {
if(DLSSOSessionHash.SSO_COOKIENAME.equals(clist[i].getName())){
dlssoToken = URLEncoder.encode(clist[i].getValue());
}
}
String nodeName = UIGlobals.getNodeName();
String ipaddr = Manager.getProperties("si_config").getProperty("CUSTOM_WEBAPP_HOST_ADDR");
String port = Manager.getProperties("si_config").getProperty("CUSTOM_WEBAPP_HTTP_PORT");
String httpsport = Manager.getProperties("si_config").getProperty("CUSTOM_WEBAPP_HTTPS_PORT");
String queryParams = "&userName="+userName+"&dlssoToken="+dlssoToken+"&nodeName="+nodeName;
%>
We have configured the host and port details where the custom web app deployed in sandbox.cfg and we get those details and call the custom web app passing the query parameters( username,dlsso token, node name) in a new browser window.
If we have deployed some custom HelloServlet web application and configured the hostname and port details about the custom web application in sandbox.cfg, the url link for the custom web application opened up on new browser window for SSO authentication would be as below,
http://ipaddr:port/HelloServlet/hello?&userName=admin&dlssoToken=U2Vzc2lvbklEbm9kZTA0bm52bXZxczhhNmJtMG0yMDkzY253cDQxMA%3D%3D&nodeName=node1
Custom WebAPP authenticating with B2Bi SSO API
Custom Web application would call the REST SSO API to check if user is authenticated.
When we get a request, we get the request parameters and check if we want to do an SSO
Authentication with B2Bi.
Below is the sample code to check if we want to call REST SSO API,
String userName = parameters.get("userName");
String nodeName = parameters.get("nodeName");
String ssoToken = parameters.get("dlssoToken");
return !StringUtils.isEmpty(userName) && !StringUtils.isEmpty(nodeName) && !StringUtils.isEmpty(ssoToken);
If we see the request contains username, nodename, dlssotoken we would call the SSO REST API to authenticate the user.
Below sample code would make a REST call and parse the json string response to json object and check if the user is authenticated.
REST API –
POST https://ipaddr:port/restwar/restapi/v1.0/authenticate/sso
userName=<USER NAME>
dlssoToken=<DLSSO TOKEN>
nodeName=<NODE NAME>
This API takes user name, dlsso token and node name as input and return JSON as output.
Sample Response from SI -
response -- > {"user": {
"authenticated": "true",
"groups": [
"super",
"notifications",
"DEPLOYMENT",
"mbiusers",
"mboxadmins",
"ACCOUNTS",
"OPERATIONS",
"SERVICES",
"MAILBOX",
"SSH",
"ADVANCED_SETUP",
"EBXML",
"BPMONITOR",
"ENVELOPES",
"MAPS",
"WEB_EXTENSIONS",
"WEB_SERVICES",
"ADAPTER_UTILITIES"
],
"name": "admin"
}}
Sample code for SSO Authentication
We used apache wink as rest client for this sample.
private static boolean authenticateSSO(Map<String, String> parameters) {
org.apache.wink.client.ClientConfig clientConfig = new org.apache.wink.client.ClientConfig();
/**
* clientConfig.setBypassHostnameVerification(true);
* used only to bypass hostname check for self signed certificates used for internal testing
* PRODUCTION- do not use this
*
*/
clientConfig.setBypassHostnameVerification(true);
org.apache.wink.client.RestClient client = new org.apache.wink.client.RestClient(clientConfig);
StringBuilder api = new StringBuilder( "https://ipaddr:port/restwar/restapi/v1.0/authenticate/sso");
//get the required query params from request
String userName = parameters.get("userName");
String dlssoToken = parameters.get("dlssoToken");
String nodeName = parameters.get("nodeName");
org.apache.wink.client.Resource resource = client
.resource(api.toString()).accept("application/json");
resource.contentType("application/x-www-form-urlencoded");
StringBuilder body = new StringBuilder("userName=").append(userName).append("&").append("dlssoToken=").append(dlssoToken).append("&").append("nodeName=").append(nodeName);
/**
* trustAllCertificates()
* used only to trust all certificates used for internal testing
* PRODUCTION - we should not use this
*/
trustAllCertificates();
String resp = resource.post(String.class,body.toString());
System.out.println(" response -- > " + resp);
JSONParser jsonParser = new JSONParser();
JSONObject jsonResponse = null;
try {
jsonResponse = (JSONObject) jsonParser.parse(resp);
} catch (ParseException e) {
e.printStackTrace();
}
//Check the response if user is authenticated
JSONObject errorObj = (JSONObject) jsonResponse.get( "error" );
if ( errorObj != null ) {
String code = (String) errorObj.get( "code" );
String description = (String) errorObj.get( "description" );
String msg = (String) errorObj.get( "message" );
System.out.println("Error returned from SSO authentication API: code="+ code + " description="+ description+ " message=" + msg );
return false;
}
JSONObject userObj = (JSONObject) jsonResponse.get( "user" );
if ( userObj == null ) {
System.out.println("\"Unrecognized response from SSO authentication API. Expected field not found: 'user'\"");
return false;
}
String username = (String) userObj.get( "name" );
if ( StringUtils.isEmpty( userName ) ) {
System.out.println("Unrecognized response from SSO authentication API. Expected field not found: 'name'");
return false;
}
String authenticated = (String) userObj.get( "authenticated" );
if ( StringUtils.isEmpty( authenticated ) ) {
System.out.println( "Unrecognized response from SSO authentication API. Expected field not found: 'authenticated'");
return false;
}
System.out.println("Authenticated --> " + authenticated);
if ( !Boolean.parseBoolean( authenticated ) ) {
// Authentication failed
System.out.println( "SSO authentication failed: userName=" + username);
return false;
}
return true;
}
Custom Webapp authenticating with B2Bi useraccount
If we need to login to custom webapplication by using any user account of B2Bi, we can call REST API exposed from B2Bi by passing the username and password details entered from the login page to the B2Bi Rest API
REST Authentication API
These API are used for authentication. When a user is authenticated a JSESSION ID will be returned in the response header. This JSESSION ID should be used in subsequent API calls.
POST https://9.113.211.25:21097/restwar/restapi/v1.0/authenticate
userName=<USER NAME>
password=<PASSWORD>
This API takes user name and password as input and return JSON as output.
Sample Output
{"user": {
"authenticated": "true",
"groups": [
"super",
"spe_admin",
],
"name": "admin"
}}
Sample code for Authentication
private static boolean authenticateLogin(Map<String, String> parameters) {
org.apache.wink.client.ClientConfig clientConfig = new org.apache.wink.client.ClientConfig();
/**
* clientConfig.setBypassHostnameVerification(true);
* used only to bypass hostname check for self signed certificates used for internal testing
* PRODUCTION- do not use this
*
*/
clientConfig.setBypassHostnameVerification(true);
org.apache.wink.client.RestClient client = new org.apache.wink.client.RestClient(clientConfig);
StringBuilder api = new StringBuilder( "https://ipaddr:port/restwar/restapi/v1.0/authenticate");
//get the username and password from the request
String username = parameters.get("un");
String password = parameters.get("pw");
org.apache.wink.client.Resource resource = client
.resource(api.toString()).accept("application/json");
resource.contentType("application/x-www-form-urlencoded");
StringBuilder body = new StringBuilder("userName=").append(username).append("&").append("password=").append(password);
/**
* trustAllCertificates()
* used only to trust all certificates used for internal testing
* PRODUCTION - we should not use this
*/
trustAllCertificates();
String resp = resource.post(String.class,body.toString());
System.out.println(" response -- > " + resp);
JSONParser jsonParser = new JSONParser();
JSONObject jsonResponse = null;
try {
jsonResponse = (JSONObject) jsonParser.parse(resp);
} catch (ParseException e) {
e.printStackTrace();
}
JSONObject errorObj = (JSONObject) jsonResponse.get( "error" );
if ( errorObj != null ) {
String code = (String) errorObj.get( "code" );
String description = (String) errorObj.get( "description" );
String msg = (String) errorObj.get( "message" );
System.out.println("Error returned from Login authentication API: code="+ code + " description="+ description+ " message=" + msg );
return false;
}
JSONObject userObj = (JSONObject) jsonResponse.get( "user" );
if ( userObj == null ) {
System.out.println("\"Unrecognized response from Login authentication API. Expected field not found: 'user'\"");
return false;
}
String userName = (String) userObj.get( "name" );
if ( StringUtils.isEmpty( userName ) ) {
System.out.println("Unrecognized response from Login authentication API. Expected field not found: 'name'");
return false;
}
String authenticated = (String) userObj.get( "authenticated" );
if ( StringUtils.isEmpty( authenticated ) ) {
System.out.println( "Unrecognized response from Login authentication API. Expected field not found: 'authenticated'");
return false;
}
System.out.println("Authenticated --> " + authenticated);
if ( !Boolean.parseBoolean( authenticated ) ) {
// Authentication failed
System.out.println( "Login authentication failed: userName=" + userName);
return false;
}
return true;
}
#DataExchange#IBMSterlingB2BIntegratorandIBMSterlingFileGatewayDevelopers